Powershell Copy-Item not copying even when folder exists - powershell

I have two consecutive calls to Copy-Item but the second one is not copying the file. Both are copying the same file to two different folders. Both folders exist. But the second one never copies. I tried commenting out the first one, but the second one still does not copy.
$deliveryFolder = "C:\Users\administrator.GTCS\Desktop\Delivery\1.5.6.1140\"
$localFolder = "C:\WebServerGateway\"
$fileSharingServerManifestFileName = "GTCS_GCS_FileSharingServer_Manifest.xml"
Copy-item -Path $localFolder$fileSharingServerManifestFileName -Destination $deliveryFolder"GcsData\Configurations\Platforms\DEV\Web Server Gateway" -Force
Copy-item -Path $localFolder$fileSharingServerManifestFileName -Destination $deliveryFolder"GcsData\Configurations\Platforms\INT\Web Server Gateway" -Force

If you are going to combine strings in this manner you need to wrap them in double quotes, to expand the variables:
$deliveryFolder = "C:\Users\administrator.GTCS\Desktop\Delivery\1.5.6.1140\"
$localFolder = "C:\WebServerGateway\"
$fileSharingServerManifestFileName = "GTCS_GCS_FileSharingServer_Manifest.xml"
Copy-item -Path "$localFolder$fileSharingServerManifestFileName" -Destination "$($deliveryFolder)GcsData\Configurations\Platforms\DEV\Web Server Gateway" -Force
Copy-item -Path "$localFolder$fileSharingServerManifestFileName" -Destination "$($deliveryFolder)GcsData\Configurations\Platforms\INT\Web Server Gateway" -Force
Notice I uses a subexpression $() in the -Destination argument. Because in your case the parser may have trouble determining where the variable name ends, therefore the string may not expand properly.
That said, neither of the arguments are particularly pretty/readable so I'd advise against this syntax. concatenating or better yet using Join-Path to derive the arguments are better choices:
$Path = $localFolder + $fileSharingServerManifestFileName
$Dest = $deliveryFolder + "GcsData\Configurations\Platforms\INT\Web Server Gateway"
Note: Concatenation forces you to pay attention to backslashes separating what would be the -Parent/-Child arguments in Join-Path
Or:
$Path = Join-Path $localFolder $fileSharingServerManifestFileName
$Dest = Join-Path $deliveryFolder "GcsData\Configurations\Platforms\INT\Web Server Gateway"
Then simply modify the commands:
Copy-item -Path $Path -Destination $Dest -Force
Copy-item -Path $Path -Destination $Dest -Force

Related

Powershell move files and folders based on older than x days

I am new to powershell and trying to learn a basic file move from one directory to another. My goal is to move files and folders that are over 18months old to cold storage folder run as a scheduled Task. I need to be able to easily modify it's directories to fit our needs. It needs to preserve the folder structure and only move files that fit the above parameters. I also need it to log everything it did so if something is off i know where.
If I run this it just copies everything. If I comment out the %{Copy-Item... then it runs and lists only based on my parameters and logs it. Where am I going wrong or am I way off base?
Yes it would be easy to use robocopy to do this but I want to use powershell and learn from it.
#Remove-Variable * -ErrorAction SilentlyContinue; Remove-Module *; $error.Clear();
#Clear-Host
#Days older than
$Days = "-485"
#Path Variables
$Sourcepath = "C:\Temp1"
$DestinationPath = "C:\Temp2"
#Logging
$Logfile = "c:\temp3\file_$((Get-Date).ToString('MM-dd-yyyy_hh-mm-ss')).log"
#transcript logs all outputs to txt file
Start-Transcript -Path $Logfile -Append
Get-ChildItem $Sourcepath -Force -Recurse |
Where-Object {$_.LastwriteTime -le (Get-Date).AddDays($Days)} |
% {Copy-Item -Path $Sourcepath -Destination $DestinationPath -Recurse -Force}
Stop-Transcript
Problem
Copy-Item -Path $Sourcepath -Destination $DestinationPath -Recurse -Force
You always specify the same path for source and destination. With parameter -recurse you will copy the whole directory $SourcePath for each matching file.
Solution
You need to feed the output of the previous pipeline steps to Copy-Item by using the $_ (aka $PSItem) variable, basically using Copy-Item in single-item mode.
Try this (requires .NET >= 5.0 for GetRelativePath method):
Get-ChildItem $Sourcepath -File -Force -Recurse |
Where-Object {$_.LastwriteTime -le (Get-Date).AddDays($Days)} |
ForEach-Object {
$relativeSourceFilePath = [IO.Path]::GetRelativePath( $sourcePath, $_.Fullname )
$destinationFilePath = Join-Path $destinationPath $relativeSourceFilePath
$destinationSubDirPath = Split-Path $destinationFilePath -Parent
# Need to create sub directory when using Copy-Item in single-item mode
$null = New-Item $destinationSubDirPath -ItemType Directory -Force
# Copy one file
Copy-Item -Path $_ -Destination $destinationFilePath -Force
}
Alternative implementation without GetRelativePath (for .NET < 5.0):
Push-Location $Sourcepath # Base path to use for Get-ChildItem and Resolve-Path
try {
Get-ChildItem . -File -Force -Recurse |
Where-Object {$_.LastwriteTime -le (Get-Date).AddDays($Days)} |
ForEach-Object {
$relativeSourceFilePath = Resolve-Path $_.Fullname -Relative
$destinationFilePath = Join-Path $destinationPath $relativeSourceFilePath
$destinationSubDirPath = Split-Path $destinationFilePath -Parent
# Need to create sub directory when using Copy-Item in single-item mode
$null = New-Item $destinationSubDirPath -ItemType Directory -Force
# Copy one file
Copy-Item -Path $_ -Destination $destinationFilePath -Force
}
}
finally {
Pop-Location # restore previous location
}
On a side note, $Days = "-485" should be replaced by $Days = -485.
You currently create a string instead of a number and rely on Powershell's ability to automagically convert string to number when "necessary". This doesn't always work though, so better create a variable with the appropriate datatype in the first place.

I have a Issue with Copy-Item in Powershell

My problem is that powershell always gives me the error msg :"cant overwrite file with itself" and i don't know why
$source=$textBox1.text
$dest=$textBox2.Text
$usr=$textBox3.text
#######From
$fNoBackup = "\\$source\c$\Users\$usr\AppData\Local\NoBackup\*"
$fData = "\\$source\c$\Users\$usr\AppData\Local\Lotus\Notes\Data\*"
$fCustomDestinations = "\\$source\c$\Users\$usr\AppData\Roaming\Microsoft\Windows\Recent\CustomDestinations\*"
$fAutomaticDestinations = "\\$source\c$\Users\$usr\AppData\Roaming\Microsoft\Windows\Recent\AutomaticDestinations\*"
#######To
$tNoBackup = "\\$dest\c$\Users\$usr\AppData\Local\NoBackup"
$tData = "\\$dest\c$\Users\$usr\AppData\Local\Lotus\Notes\Data"
$tCustomDestinations = "\\$dest\c$\Users\$usr\AppData\Roaming\Microsoft\Windows\Recent\CustomDestinations"
$tAutomaticDestinations = "\\$dest\c$\Users\$usr\AppData\Roaming\Microsoft\Windows\Recent\AutomaticDestinations"
#######le copy
#############"Touch" the files ;D
New-Item -ItemType File -Path $tNoBackup -Force
New-Item -ItemType File -Path $tData -Force
New-Item -ItemType File -Path $tCustomDestinations -Force
New-Item -ItemType File -Path $tAutomaticDestinations -Force
#############Copy
Copy-Item $fNoBackup $tNoBackup -Recurse -Force
Copy-Item $fData $tData -Recurse -Force
Copy-Item $fCustomDestinations $tCustomDestinations -Recurse -Force
Copy-Item $fAutomaticDestinations $tAutomaticDestinations -Recurse -Force
To debug this, I would recommend that you print out the contents of your variables to the console. That way, you will understand what the problem is.
I don't know Powershell, but I am seeing something which could be a problem. Obviously, $ within a string has a special meaning (it seems to denote variables). Therefore, I suspect that you have to quote (escape) the $ sign if you want it to be literal in the string.
In every of your strings, you have c$. That should probably be c`$ since ` is the escape character in Powershell. When you have done this, print the strings out to the console again to make sure you have got them right.

Test-Path Move-Item Problems

I run this PowerShell script, and it works fine on PowerShell 4.0. But I now have PowerShell 5.0 and the script does work but it throws an error:
The Script:
$path = "X"
$destination = "Y"
while (Test-Path -Path $path) {
Move-Item -Path "$path\*zip" -Destination "$destination"
}
The error I get is:
Move-Item : The process cannot access the file because it is being
used by another process.
The title of the question: "Test-Path Move-Item Problems" implies that one cmdlet might be impacting the other. That doesn't make sense to me as Test-Path is checking the folder's existence and Move-Item is working on child items within that folder.
Personally I would not use a while loop for this use case as, once you have determined that the path exists you don't need to keep testing it:
if(Test-Path -Path $path){
Move-Item -Path $path\*zip -Destination $destination
}
just do it
Move-Item -Path "$path\*zip" -Destination "$destination" -ErrorAction Ignore

Powershell Copy-Item going up few directories

I am trying to provide a path to Copy-Item cmdlet in Powershell which goes up few directories but I am getting error
"ERROR - failed with error: "A positional parameter cannot be found
that accepts argument '......\'."
The command I am trying to execute is
Copy-Item $Source + "..\..\..\" + ($environment) + "\*.config" $destination
Can anyone please guide me how can I go up few directories while providing a path to Copy-Item
You need to parenthese the first argument (source):
Copy-Item ($Source + "..\..\..\" + ($environment) + "\*.config") $destination
Consider using the Join-Path cmdlet when combining a path. You could also write something like:
$sourceDir = Join-Path (Get-Item $Source).Parent.Parent.Parent $environment
Get-ChildItem -Path $sourceDir -Filter '*.config' | Copy-Item -Destination $destination
I just had to put double quotes around the whole path rather than concatenating.
Copy-Item "$Source\..\..\..\$environment\*.config" $destination

Powershell Remove-Item IF file already exists after Copy-item

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
}
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 }
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
}
Test-Path commandlet will help you check if the file exists
http://technet.microsoft.com/en-us/library/ee177015.aspx
#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