PowerShell командлет Copy-Item используется для копирования файлов между локальными, сетевыми каталогами или между компьютерами по сети через WinRM. Командлет Copy-Item предоставляет большое количество опций, которые можно использовать в разных сценариях копирования файлов и каталогов (по своим возможностям этот командлет почти не уступает утилите robocopy). Например:
- перезапись файлов (override)
- фильтрация по имени/шаблону
- исключение по имени/шаблону
- Verbose режим
- Копирование файлов с/на удаленные компьютеры
Начнем с простых примеров использования Copy-Item и будем переходить к более сложным.
Содержание:
- Копирование файлов и каталогов
- Копирование с заменой и копирование с заменой read-only файлов
- Копирование с фильтрацией по шаблону
- Исключение файлов при копировании
- Копирование файлов на удаленный компьютер по сети
- Ключ PassThru
- Ключ Verbose
- Несколько полезных скриптов с Copy-Item
Копирование файлов и каталогов
Чтобы скопировать один файл 1.txt из каталога C:SourceFolder в F:DestFolder, выполните:
Copy-Item -Path "C:SourceFolder1.txt" -Destination "F:DestFolder1.txt"
Можно использовать сокращенный синтаксис командлета, пропустив указание параметров Path и Destination:
cpi "C:SourceFolder1.txt" "F:DestFolder1.txt"
Теперь скопируем каталог C:SourceFolderfolder в F:DestFolderfolder. В папке folder находится файл 1.txt. Обратите внимание что без ключа –Recurse, папка folder копируется без содержимого:
Copy-Item -Path "C:SourceFolderfolder" -Destination "F:DestFolderfolder" -Recurse
С помощью Copy-Item также можно просто объединить файлы из несколько директорий в одну (слияние директории), для этого нужно перечислить директории в ключе –Path:
Copy-Item -Path "C:SourceFolder*", "C:SourceFolder2*", "C:SourceFolder3*" -Destination "F:DestFolder"
Копирование с заменой и копирование с заменой read-only файлов
Copy-Item по умолчанию при копировании заменяет файлы в целевом каталоге. Никаких дополнительных параметров указывать не нужно. При копировании каталога, если нужно заменить каталог в целевой папке, нужно использовать ключ –Force, иначе будет ошибка “Элемент folder с указанным именем уже существует — DirectoryExists”.
Для перезаписи файла с атрибутом read-only, нужно использовать ключ -Force. Если его не использовать, вы получите ошибку “отказано в доступе по пути… CopyFileInfoItemUnauthorizedAccessError”.
Чтобы скопировать файл с перезаписью файла с read-only атрибутом используйте параметр Force.
Copy-Item -Path "C:SourceFolder1.txt" -Destination "F:DestFolder1.txt" -Force
Совет. Чтобы не путаться, ключ –Force можно рассматривать как ключ для копирования с заменой.
Чтобы Copy-Item скопировал файлы из одной папки в другую без замены существующих файлов, можно использовать этот простой скрипт
Copy-Item (Join-Path "C:SourceFolder" "*") "F:DestFolder" -Exclude (Get-ChildItem "F:DestFolder") -Recurse
Этот скрипт скопирует все файлы и папки из C:SourceFolder в F:DestFolder без замены файлов уже существующих в F:DestFolder
Копирование с фильтрацией по шаблону
С помощью Copy-Item можно скопировать файлы/директории выбранные с помощью wildcard символа * или с помощью символа ?. Также поддерживаются некоторые регулярные выражения
- * — обозначает любое количество любых символов
- ? – обозначает 1 любой символ
- [a-z], [0-9] – символы между a-z и цифры между 0 и 9
Для примера возьмём такую структуру файлов:
Выполним копирование командой:
Copy-Item -Path "C:SourceFolderfol*" -Destination "F:DestFolder"
Результат в F:DestFolder
Теперь чистим папку назначения и выполняем:
Copy-Item -Path "C:SourceFolderfolder[0-3]" -Destination "F:DestFolder"
Результат:
Папка без цифры в окончании не скопировалась, потому что folder[0-3] подразумевает что после folder будет как минимум еще 1 символ между 0 и 3
Исключение файлов при копировании
С помощью ключа –Exclude можно исключить файлы при копировании. Например, следующай команда скопирует все файлы кроме файлов с расширением txt.
Copy-Item -Path "C:SourceFolder*" -Destination "F:DestFolder" -Recurse -Force -Exclude "*.txt"
Аналогичным же образом можно применить ключ –Include, например
Copy-Item -Path "C:SourceFolder*" -Destination "F:DestFolder" -Recurse -Force -Include "*.txt"
Скопирует только txt файлы. Хотя для простоты гораздо удобнее использовать при копировании вид
-Path "C:SourceFolder*.txt"
.
Копирование файлов на удаленный компьютер по сети
Copy-File может копировать не только по SMB протоколу, но и через WinRM (WSMan).
Создайте новую сессию с компьютером testnode1 и выполните копирование в её контексте:
$session = New-PSSession -ComputerName testnode1
Copy-Item -Path "C:SourceFolder*" -ToSession $session -Destination "C:SourceFolder" -Recurse -Force
Эта команда скопирует файлы с локального компьютера из директории C:SourceFolder на компьютер testnode1 в C:SourceFolder.
Примечание. Доступность WSMan на удаленном компьютере можно проверить с помощью командлета Test-WSMan.
Test-WSMan -ComputerName testnode1
Если WSMan не настроен, вы можете выполнить его быструю конфигурацию. Для этого откройте командную строку с правами администратора и выполните
winrm quickconfig
Также можно копировать и через обычные сетевые SMB шары, для этого просто используйте UNC формат сетевого пути.
Copy-Item -Path "C:SourceFolder*" -Destination "\testnode1C$copy_tutorial"
Можно скопировать файл с удаленного компьютера. Принцип такой же, как и при копировании файлов на удаленный компьютер, за исключением параметра –ToSession, вместо него нужно использовать –FromSession:
$session = New-PSSession -ComputerName testnode1
Copy-Item -FromSession $session -Path "C:SourceFolder*" -Destination "F:DestFolder" -Recurse -Force
Эта команда скопирует содержимое папки C:SourceFolder с компьютера testnode1 на локальный компьютер в директорию F:DestFolder
Ключ PassThru
Командлет Copy-Item (как и многие другие командлеты PowerShell) не возвращает результатов в консоль. Параметр PassThru применяется скриптах, или для лог-файлов, когда нужно получить список скопированных файлов и работать с ним дальше. Рассмотрим пример
$items = Copy-Item -Path "C:SourceFolder*" –Destination "\testnode1C$copy_tutorial" -PassThru
Переменная
$items
будет содержать список скопированных файлов, с которым вы можете работать дальше.Это значит что вы можете напрямую работать с этими файлами. Например выполнив команду
Remove-Item $items[0]
, вы удалите директорию folder.
Ключ Verbose
При использовании ключа -Verbose вы получите подробный лог операций копирования. Например, вывод команды
Copy-Item -Path "C:SourceFolder*.txt" -Destination "F:DestFolder" -Recurse -Force -Verbose
Несколько полезных скриптов с Copy-Item
Скопировать только файлы:
Get-ChildItem "C:SourceFolder" -File -Recurse | Copy-Item -Destination "F:DestFolder"
Скопировать структуру папок, без файлов:
$path = Get-ChildItem "C:SourceFolder" -Recurse | ?{$_.PsIsContainer -eq $true}
$dest = "F:DestFolder"
$parent = $path[0].Parent.Name
$path | foreach {
$_.FullName -match "$parent.+"
New-Item -ItemType directory ($dest + $Matches[0])
}
Copy-Item очень простой и удобный в использовании командлет PowerShell для выполнения операций копирования и перемещения файлов. В сочетании с другими инструментами PowerShell, Copy-Item также является мощным инструментом для написания скриптов.
У меня есть сценарий резервного копирования, который копирует несколько каталогов файлов в папку для резервного копирования. К сожалению, не все файлы в папке доступны. История состоит в том, что они были заархивированы, и что осталось, это имя файла с серым крестиком на нем. Когда я пытаюсь скопировать его, я получаю следующее сообщение:
Copy-Item : Access to the path 'E:BackupLocDOzOLD_Under Review for NOT USED_keep for now2006-06N.doc' is denied.
At C:UsersmeDocumentspowershellFilesBackup.ps1:13 char:4
+ Copy-Item -Path $SourcePath -Destination $DestinationPath -Force - ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : PermissionDenied: (N.doc:FileInfo) [Copy-Item], UnauthorizedAccessException
+ FullyQualifiedErrorId : CopyDirectoryInfoItemUnauthorizedAccessError,Microsoft.PowerShell.Commands.CopyItemCommand
Да, это жалоба в местоположении, а не в месте. Я открыл каталог / файлы, поэтому он не только для чтения, но все равно получаю это.
Кроме того, преодоление этих ошибок копирования занимает очень много времени. Если бы я мог избежать попыток скопировать эти файлы, это было бы намного быстрее. Там, вероятно, 200 из этих файлов, которые заархивированы.
Однако я копирую папку, а не имена файлов по отдельности. Есть те, которые не заархивированы в этой папке. Там нет плана, чтобы очистить их. Я пытаюсь определить, когда происходит ошибка, но она достигает моей точки останова только в том случае, если оператор $error.Exception -ne $null после того, как он записывает ошибки на экран и принимает их навсегда (см. Комментарий).
Любая идея, как я могу отфильтровать те, которые заархивированы, или захватить их и проверить их по массиву или списку, чтобы я не получил сообщение об ошибке? Я не понял, как найти их, как они происходят, так как он копирует всю папку.
Я смотрел на проверку ошибок во время копирования, но я не вижу, как применить это к моей проблеме.
Это метод копирования:
function CopyFileToFolderUNC($SourcePath, $DestinationPath){
if(-Not (Test-Path $SourcePath))
{
$global:ErrorStrings.Add("Exception: No such path, $SourcePath;; ")
write-output "++ Error: An error occured during copy operation. No such path, $SourcePath ++"
}
Copy-Item -Path $SourcePath -Destination $DestinationPath -Force -Recurse -errorVariable errors
foreach($error in $errors)
{
if ($error.Exception -ne $null)
{
$global:ErrorStrings.Add("Exception: $($error.Exception);; ")
write-output "++ Error: An error occured during copy operation. Exception: $($error.Exception) ++" #this breakpoint is hit after giving errors on screen and taking a long time/failing to copy files it can't reach
}
write-output "Error: An error occured during copy operation. Exception: $($error.Exception)"
}
}
Это моя последняя попытка, основанная на том, что было предложено @theo, но она пытается скопировать файлы, которые я не проверял, для файла, который я могу скопировать, только каталог над ним:
function CopyFileToFolderUNC($SourcePath, $DestinationPath, $exclude){
if(-Not (Test-Path $SourcePath )) #-PathType Container
{
$global:ErrorStrings.Add("Exception: No such path, $SourcePath;; ")
write-output "++ Error: An error occured during copy operation. No such path, $SourcePath ++"
}
#$tempFileName = [System.IO.Path]::GetFileName($SourcePath)
#$tempDestFileNamePath = "$DestinationPath$tempFileName"
Get-ChildItem -Path $SourcePath -Recurse -Force | ForEach {$_} {
#test if maybe we are dealing with an off-line file here
#or use the enum name 'Offline'
# or use the numeric value: 4096
#$oldAttribs = $null
$attr = $_.Attributes.value__
write-output "++ $_ $attr ++"
if (($_.Attributes -eq [System.IO.FileAttributes]::Offline) -or ($_.Attributes.value__ -eq "4096")) {
$_.Attributes=[System.IO.FileAttributes]::Normal
#$oldAttribs = $_.Attributes
#make it a 'normal' file with only the Archive bit set
#$_.Attributes = [System.IO.FileAttributes]::Archive
#log that the file was an issue and copy the other ones
$global:ErrorStrings.Add("Found offline file in backup dir, $_. Logging info and not copying this file. Offline. Please investigate.;; ")
write-output "++ Error: An error occured during copy operation. No such path or file, Offline $_ ++"
} #if
elseif(($_.Attributes -eq [System.IO.Fileattributes]::Archive) -or ($_.Attributes.value__ -eq "32")) {
$global:ErrorStrings.Add("Found offline file in backup dir, $_. Logging info and not copying this file. Archive. Please investigate.;; ")
write-output "++ Error: An error occured during copy operation. No such path or file, Archive $_ ++"
} #elseif
elseif(($_.Attributes -eq [System.IO.Fileattributes]::SparseFile) -or ($_.Attributes.value__ -eq "512")) {
$global:ErrorStrings.Add("Found offline file in backup dir, $_. Logging info and not copying this file. SparseFile. Please investigate.;; ")
write-output "++ Error: An error occured during copy operation. No such path or file, SparseFile $_ ++"
} #elseif
elseif(($_.Attributes -eq [System.IO.Fileattributes]::ReparsePoint) -or ($_.Attributes.value__ -eq "1024")) {
$global:ErrorStrings.Add("Found offline file in backup dir, $_. Logging info and not copying this file. ReparsePoint. Please investigate.;; ")
write-output "++ Error: An error occured during copy operation. No such path or file, ReparsePoint $_ ++"
} #elseif
else {
#the file is not or no longer off-line, so proceed with the copy
$_ | Copy-Item -Destination $DestinationPath -Force -Recurse -ErrorVariable errors
foreach($error in $errors)
{
if ($error.Exception -ne $null)
{
$global:ErrorStrings.Add("Exception: $($error.Exception);; ")
write-output "++ Error: An error occured during copy operation. Exception: $($error.Exception) ++"
}
write-output "Error: An error occured during copy operation. Exception: $($error.Exception)"
}
} #else
#if ($oldAttribs) {
# $_.Attributes = $oldAttribs
#}
} #Get-ChildItem
Например, я тестирую каталог в \drivefolderFormsC Forms и он говорит, что это хороший атрибут «16», но в этом каталоге есть файл, который копия пытается скопировать в мою папку dir, и я вижу, что он имеет это для атрибутов: file.pdf Архив, SparseFile, ReparsePoint, Offline. Но я не проверяю этот файл, последней вещью, для которой я проверял атрибуты, был каталог, в котором он находится.
Только вышел из отпуска, и уже аврал на работе, но время для PowerShell я всё равно выкроил 😉
Вот например комрад Camelot подкинул следующую, интересную задачку: Скопировать n файлов, и вывести список нескопированных в текстовый файл (ну либо предварительно попробовать скопировать неудачников еще разок).
Copy-Item .test* c:windowssystem32 -ErrorAction "silentlyContinue" -ErrorVariable Failed
Этой командой файлы будут скопированы из одного места в другое, при этом ошибки не будут выдаваться на экран, но будут помещены в массив $Failed.
Теперь посмотрим содержимое одного из элементов этого массива с помощью команды Format-List с ключем -Force:
$Failed[0] | fl * -force
Станет видно что нам необходимо свойство TargetObject в котором содержится объект файла который не удалось скопировать. Поместим эти объекты в переменную $Files
$Files = $Failed | foreach {$_.TargetObject}
Ну и теперь можно поступать с ними по своему усмотрению, либо просто попробовать скопировать их еще раз:
Copy-Item $Files c:windowssystem32
либо поместить их полные пути в текстовый файл:
$Files | foreach {$_.fullname} | Set-Content log.txt
Вот собственно и всё 🙂
- Remove From My Forums
-
Question
-
When I used Copy-Item to copy a folder from my local machine to a remote machine, I got the following error. Some files are not copied. But all the files are copied with no error when I copied them by using copy/paste in Windows Explorer. Any suggustion?
ThanksCopy-Item : The specified network name is no longer available.
At H:ScriptsPowerShellRemoteCopyFiles.ps1:33 char:14
+ Copy-Item <<<< $Source $Path -recurse -force
+ CategoryInfo : WriteError: (RICHED20.DLL:FileInfo) [Copy-Item], IOException
+ FullyQualifiedErrorId : CopyDirectoryInfoItemIOError,Microsoft.PowerShell.Commands.CopyItemCommand
По умолчанию Copy-item ничего не возвращает. Есть вариант узнать как прошло копирование, это проверить переменную $error
Copy-item $From $To
IF ($Error[0].CategoryInfo.Activity -eq ‘Copy-Item’)
{
$inf = «==ERROR== Копирование » + $To + » не удачно » + $Error[0]
Write-host $inf -foreground «RED»
}
ELSE
{
$inf = «Копирование » + $To + » Прошло успешно»
Write-host $inf -foreground «green»
}
Есть и другой вариант, смотреть ловушки(trap)
Про ловушки подробней тут и тут
Function Copy-check($From , $To)
{
trap {
$err = «TRAP ###ERROR### » + $error[0] #.FullyQualifiedErrorId
write-host From_trap=== $err
return $err
}
Copy-item $From $To -Recurse -ea 1
Return 0
}
Copy-check $From $To
Об авторе Alex
Работаю Администратором. В основном Windows. Специализируюсь на Hyper-V
Запись опубликована в рубрике Powershell с метками Powershell. Добавьте в закладки постоянную ссылку.