17

I need to add a safety net in my script. I'm trying to do a copy job based on a list of users provided through a txt file. Copy the files from that users home directory to a new location. Once the files are copied, check if the file exists in the new location. If yes, then Remove-Item.

Can someone help me? I just don't know how to implement the "if file exists" logic.

$username = Get-Content '.\users.txt'
foreach ($un in $username)
{
  $dest = "\\server\homedirs\$un\redirectedfolders"
  $source = "\\server\homedirs\$un"
  New-Item -ItemType Directory -Path $dest\documents, $dest\desktop

  Get-ChildItem $source\documents -Recurse -Exclude '*.msg' | Copy-Item -Destination $dest\documents
  Get-ChildItem $source\desktop -Recurse -Exclude '*.msg' | Copy-Item -Destination $dest\desktop

  Get-ChildItem $source\mydocuments, $source\desktop -Recurse -Exclude '*.msg' | Remove-Item -Recurse
}

4 Answers 4

42

The shortest way to delete file if it doesn't exist is NOT to use Test-Path but:

rm my_file.zip -ea ig

This is short version of

rm my_file.zip -ErrorAction Ignore

which is much more readable and more DRY then

if (Test-Path my_file.zip) { rm my_file.zip }

Sign up to request clarification or add additional context in comments.

6 Comments

Not only is this more readable, it is also more correct. The pattern of testing for existence first is wrong because by the time Test-Path returns with a positive answer, the folder might already have been deleted, at which point calling a vanilla Remove-Item will throw an error.
I wasn't even aware of that and and you are totally right. One additional benefit is that it also clearly states the intention of the programmer - if I want to delete, testing for something before is a noise.
On second thought, the problem here is that it might hide a different error. For example, the file might be there, but a permissions issue prevents you from deleting it. I'm not a PS expert, but a quick search turns up ErrorVariable which may come in useful in disambiguating such cases.
ErrorVariable or use -ea 0 (silently continues but records error) and check Error afterwards.
Having to check ErrorVariable defeats the purpose of making it shorter than with Test-Path. I like to do it this way: Get-ChildItem * -Include my_file.zip | Remove-Item. It saves me having to specify the filename twice
|
7

To answer your question per se, you can do it like this:

Get-ChildItem $source\mydocuments, $source\desktop -Recurse -Exclude '*.msg' | %{
  if (Test-Path ($_. -replace "^$([regex]::escape($source))","$dest")) {
    Remove-Item $_ -Recurse
  }
}
  • Test-Path returns $true if the file at the given path exists, otherwise $false.
  • $_ -replace "^$([regex]::escape($source))","$dest" converts the path of each source item you're enumerating with the corresponding destination path, by replacing $source at the beginning of the path with $dest.
  • The basic regex for the first argument to the -replace operator is ^$source (which means "match the value of $source at the beginning of the string"). However, you need to use [regex]::escape in case $source contains any regex special characters, which is in fact extremely likely with Windows paths, since they contain backslashes. For example, the value you've given here for $source contains \s, which in a regex means "any whitespace character". $([regex]::escape($source)) will interpolate the value of $source with any regex special characters properly escaped, so that you're matching the explicit value.

That said, if your purpose is to copy each item to a new location, and remove the original only if the copy to the new location is successful, it seems like you're reinventing the wheel. Why not just use Move-Item instead of Copy-Item?


Not directly related to the question, but rather than repeating the same command for each subdirectory, you can use a foreach loop:

foreach ($subdir in (echo documents desktop)) {
  # Whatever command you end up using to copy or move the items, 
  # using "$source\$subdir" and "$dest\$subdir" as the paths
}

Comments

2

Test-Path commandlet will help you check if the file exists

http://technet.microsoft.com/en-us/library/ee177015.aspx

Comments

0

@Adi Inbar, I need to use a function like this because I need to move files to a remote session, and the Move-Item does not work when I tried -ToSession... only Copy-Item. The Key is that if the power or internet goes down, the script will delete the file even if it wasn't copied.

$username = "name"
$password = ConvertTo-SecureString "password" -AsPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential -ArgumentList ($username, $password)
$Session = New-PSSession -ComputerName "IPAdress" -Credential $credential 
Copy-Item -Path C:\userPC_1\csv-output\*.csv  -Destination C:\userPC_2\Documents\Test_Scripts -ToSession $Session -Verbose
Get-PSSession | Remove-PSSession
Get-ChildItem -Path C:\userPC_1\csv-output\*.csv | Remove-Item -Force

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.