PowerShell: copy / move files based on regex value while maintaining folder structure etc.

Here's the scenario: we have a production web server with several thousand files and folders to which "_DATE" is attached. We want to move them to a temporary folder (make sure they are not used), and delete the files later.

I can use:

Get-ChildItem -Path W:\IIS -Recurse | Where-Object{$_.Name -match "_\d{8,10}$"} 

Get a list of all files / folders and their locations. But manually deleting them all seems to be a lot of work, especially if necessary in the future. I found some examples that almost do what I want:

 cls $source = "W:\IIS" $destination = "C:\Temp\DevTest" foreach ($i in Get-ChildItem -Path $source -Recurse) { if ($i.Name -match "_\d{8,10}$") { Copy-Item -Path $i.FullName -Destination $item.FullName.ToString().Replace($source,$destination).Trim($item.Name) } } 

and

 cls $source = "W:\IIS" $destination = "C:\Temp\DevTest" $bin = Get-ChildItem -Path $source -Recurse | Where-Object{$_.Name -match "_\d{8,10}$"} foreach ($item in $bin) { Copy-Item -Path $item.FullName -Container -Destination $item.FullName.ToString().Replace($source,$destination).Trim($item.Name) -Recurse } 

The problem with these two is that when I test them only with the instance instance, I get a flat directory and I need to keep the directory structure so that if the movement goes wrong, I can return (preferably by dragging and dropping all the folders back to the IIS folder), or I get a copy of a large number of additional folders / files that do not appear when I run the first command.

Modification of my first command:

 Get-ChildItem -Path W:\IIS -Recurse | Where-Object{$_.Name -match "_\d{8,10}$"} | Copy-Item -Container -Destination C:\Temp\DevTest -Recurse 

It copies everything I need, but with a flat directory tree, instead of preserving the tree structure (but replacing the source directory with the destination).

Any suggestions / comments?

+6
source share
3 answers

You can play with the following, which seems to be doing its job (although it may use some refactoring). The core of ProcessFolder(…) , which is called recursively.

 function Log { param($Error) if(!$script:log) { $script:log = join-path $dst "log$timestamp.txt" } $timestamp $timestamp >> $script:log $Error $Error >> $script:log } function CopyFile { param($Path) $tmp = join-path $dst $Path.Substring($src.Length) write-host "File src: $Path" write-host "File dst: $tmp" Try { #copy the file copy $Path $tmp -Force } Catch { Log "ERROR copying file $Path to $tmp`:` $_" } } function ProcessFolder { param($Path) $tmp = join-path $dst $Path.Substring($src.Length) write-host "Dir. src: $Path" write-host "Dir. dst: $tmp" Try { #create target directory New-Item $tmp -itemtype directory -force > $null #process files if they match dir $Path | ?{!$_.PsIsContainer -and $_.Name -match "_\d{8,10}$" } | sort | %{ CopyFile $_.FullName } #process subdirectories dir $Path | ?{$_.PsIsContainer} | sort | %{ ProcessFolder $_.FullName } #remove target directory if it contains no files on any level if( !(dir $tmp -recurse | ?{!$_.PsIsContainer}) ) { del $tmp -recurse } } Catch { Log "ERROR copying folder $Path to $tmp`:` $_" } } cls $src = "W:\IIS\" $dst = "C:\Temp\DevTest\" $log = $null $timestamp = '{0:yyyyMMddHHmmssfffffff}' -f (Get-Date) ProcessFolder $src '' 'DONE!' if( $log ) { echo "Check the log file: $log" } Read-Host 
+1
source

The first should work perfectly. It maintains a directory structure. But you have to be careful when copying folders. Assuming that you only need the files in the template in which you want, and you do not need the folders located there, you can do below:

 $source = "W:\IIS" $destination = "C:\Temp\DevTest" if(-not (Test-Path $destination)) { mkdir $destination | out-null} foreach ($i in Get-ChildItem -Path $source -Recurse) { if (($i.Name -notmatch "_\d{8,10}$") -and (-not $i.PsIsContainer)) { continue; } Copy-Item -Path $i.FullName -Destination $i.FullName.Replace($source,$destination).Trim($i.Name) } 
+2
source

Copy-Item is a mess for me, you can read other StackOverflow answers this one or this other one . Here is a solution that is a kind of "bricolage".

 $source = "W:\\IIS" # Really double \ Get-ChildItem -Path $source -Recurse | Where-Object{($_.Name -match ""_\d{8,10}$") -or ($_.psiscontainer)} | % {copy-item $_.fullname ($_.fullname -replace $source,'C:\Temp\DevTest') } 
+1
source

Source: https://habr.com/ru/post/900069/


All Articles