Test-Path Move-Item Problems - powershell

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

Related

How can i only Move-Item if the destination does not equal specified desitnation?

I'm hoping someone can help me here. I'm trying to make a Move-Item script where it will move all the "Music" folders into their parent directory unless the directory equals "C:\Temp\Users".
For example.. if the folder is located in "C:\Temp\Users\Billy Bob\Documents\Music" then the Music file will move to "C:\Temp\Users\Billy Bob\Music".
But if the "Music" folder is located at "C:\Temp\Users\Billy Bob\Music" then it won't be allowed to move as the "C:\Temp\Users" directory is specified within an "If-Statement" to block all moves into this directory.
This is what I have so far but I can't seem to get it to work for me
Get-ChildItem -Path C:\Temp\Users -Recurse -Directory -Force -Filter "Music"|
ForEach-Object {
if (Move-Item -Path $_.FullName -Destination $(Split-Path -Parent $_.PSParentPath) = C:\Temp\Users ) {
Write-Output "Move Stopped" -WhatIf
}
elseif (Move-Item -Path $_.FullName -Destination $(Split-Path -Parent $_.PSParentPath) -ne C:\Temp\Users) {
Move-Item -Path $_.FullName -Destination $(Split-Path -Parent $_.PSParentPath) -WhatIf
}
}
I have this part working but it moves all "Music" Folders to the Parent directory so I would like to add a condition to cancel any move where the destination of the move equals specifically "C:\Temp\Users". Any other destination would be okay to proceed to move the "Music" folder into the parent directory.
Get-ChildItem -Path C:\Temp\Users -Recurse -Directory -Force -Filter "Music"|
ForEach-Object {
Move-Item -Path $_.FullName -Destination $(Split-Path -Parent $_.PSParentPath) -WhatIf
}
You don't need any condition if you start searching at a deeper level within the directory tree:
Get-ChildItem -Path C:\Temp\Users\*\*\* -Recurse -Directory -Force -Filter "Music" |
Move-Item -Destination { $_.Parent.Parent.FullName } -WhatIf
The wildcard part \*\*\* forces Get-ChildItem to enumerate directories which are at least three levels deeper than C:\Temp\Users. So C:\Temp\Users\Billy Bob\Documents\Music gets enumerated, but C:\Temp\Users\Billy Bob\Music won't, because it only matches the first two * from the wildcard path.
I've also simplified the Move-Item call. You can pipe directly into Move-Item, so it gets the -Path parameter from the pipeline. For -Destination you can pass a delay-bind script block ({ ... }) argument, which makes ForEach-Object obsolete (see this answer for further details).
Also, Split-Path -Parent is not needed when working with DirectoryInfo objects from Get-ChildItem output. It has a .Parent property which contains a DirectoryInfo object as well, so by chaining you can get multiple levels up in the directory tree.

Powershell Copy-Item not copying even when folder exists

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

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.

Work around for writing Destination argument as a script block in powershell

My organization has a lot of picture files in different folders all over the network.
I've been tying to consolidate them in a "PictureLibrary" folder by the project folder they're in.
Since there are other files besides picture files in these project folders I can't just move the whole folder.
I've been trying the code below:
($Images = gci "Z:\DivisionFolder" -Recurse -file -Include "*.jpg") |
Foreach-object {
copy-Item $_.DirectoryName -Destination "Z:\DivisionFolder\PictureLibrary"
}
$a=0
For ($a; $a -lt $Images.count; $a++){
move-item $Images[$a].fullname -Destination {join-path -Path "D:\(1) PROJECTS & PORTFOLIOS\PictureLibrary" -ChildPath $Images[$a].Directoryname}
}
I've tried it in various formats but get this error
Copy-Item : Cannot evaluate parameter 'Destination' because its
argument is specified as a script block and there is no input. A
script block cannot be evaluated without input.
and I can't figure out how to write this without writing destination as a Script block
Any help would be very appreciated
Try to pass the destination argument like this:
-Destination $(. { script_block_body_here })
() . { } is Dot sourcing operator and $( ) is Subexpression operator.
($RougePictures = Get-Childitem -Path "C:\" -recurse -file -Include "*.JPG")|
Foreach-object {
copy-Item $_.DirectoryName -Destination "C:\Folders\PictureLibrary"
}
$MainFolders = get-childitem -Path "D:\(1) PROJECTS & PORTFOLIOS\PictureLibrary"
$a=0
For ($a; $a -lt $MainFolders.count; $a++) {
Foreach ($Rouge in $RougePictures){
if ((compare-object (split-path $Rouge.directoryname -leaf) $MainFolders[$a] -IncludeEqual).sideindicator -eq '=='){
move-item $Rouge.fullname -destination $MainFolders[$a].fullname
}
}
}
Found a work around for using a Script block as the destination argument.
Used
if ((compare-object (split-path $Rouge.directoryname -leaf) $MainFolders[$a] -IncludeEqual).sideindicator -eq '==')
after I copied the folders copy-Item $_.DirectoryName -Destination "C:\Folders\PictureLibrary" to find those that matched the folders the pictures are in. If it finds a match '==' the move-item moves those files.
Hope this helps.

Powershell Variable for dynamic Move-Item

I have a function that looks at some registry settings for SQL Server which just pulls put the data and log location and puts them in variables. But I run into an issue when I pass them into Move-Item. Basically:
fnGetDataNLog
Returns $datalocation, $loglocation
When I run Move-Item -Path $datalocaton -Destination $loglocation I get
Cannot bind argument to parameter 'path' because it does not exist.
Is that due to its passing a runtime variable? Is there another way to do that then.
You can run a Test-Path to ensure the location exists.
If you $Datalocation is a UNC path to a directory you can do the below. eg. $Datalocation = '\\UNC\Path\To\Folder'
If(-not (Test-Path -Path $Datalocation)) {
New-Item -Path $Datalocation -ItemType Directory
}
Move-Item -Path $Datalocation-Destination $loglocation
This will test if the path at $Datalocation exists, if -not then it will create it.
if the UNC path is a string that goes to a file, ie. not an object result from a Get-ChildItem, then you can use a bit of regex to get the parent folder and then do the below. The regex will remove everything after the last \.
eg. \\UNC\Path\To\Folder\File.txt becomes \\UNC\Path\To\Folder\
If(-not (Test-Path -Path ($Datalocation -replace '[^\\]+$'))) {
New-Item -Path $Datalocation -ItemType Directory
}
Move-Item -Path $Datalocation -Destination $loglocation