PowerShell: копирование / перемещение файлов на основе значения регулярного выражения, сохранение структуры папок и т. д
вот сценарий: у нас есть производственный веб-сервер, который имеет несколько тысяч файлов и папок, к которым прикреплен "_DATE". Мы хотим переместить их во временную папку (убедитесь, что они не используются), а затем удалить файлы.
Я могу использовать:
Get-ChildItem -Path W:IIS -Recurse | Where-Object{$_.Name -match "_d{8,10}$"}
чтобы получить список всех файлов / папок и их местоположений. Но вручную удалить их все кажется большой работой, особенно если это необходимо в будущем. Я нашел несколько примеры, которые почти делают то, что я хочу:
cls
$source = "W:IIS"
$destination = "C:TempDevTest"
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)
}
}
и:
cls
$source = "W:IIS"
$destination = "C:TempDevTest"
$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
}
проблема с этими двумя заключается в том, что когда я тестирую их с помощью только copy-item, я получаю плоский каталог, и мне нужно сохранить структуру каталогов, чтобы, если перемещение пойдет не так, я мог вернуться (предпочтительно путем перетаскивания всех папок обратно в папку IIS) или я получаю копию многих дополнительных папок/файлов, которые не появляются при запуске первой команды.
изменение моего первого команда:
Get-ChildItem -Path W:IIS -Recurse | Where-Object{$_.Name -match "_d{8,10}$"} | Copy-Item -Container -Destination C:TempDevTest -Recurse
скопирует все, что мне нужно, но с плоским деревом каталогов, а не сохранит древовидную структуру (но заменит исходный каталог назначением).
любые предложения / комментарии?
3 ответов
вы можете поиграть со следующим, что, похоже, делает работу (хотя она может использовать некоторый рефакторинг). Ядро ProcessFolder(…)
которая вызывается рекурсивно.
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
первый должен идеально работать. Он сохраняет структуру каталогов. Но вы должны быть осторожны с копированием папок. Предполагая, что вы хотите только файлы в шаблоне, который вы хотите, и вам все равно, что папки там, вы можете сделать ниже:
$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)
}
для меня Copy-Item
беспорядок вы можете прочитать другие ответы StackOverflow этот или этот . Вот решение, которое является своего рода "бриколаж".
$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') }