Powershell copy item обработка ошибок

I have a backup script that copies several directories of files to a backup location. Unfortunately, not all of the files in the folder are accessible. What the history is, is that they were archived, and what’s left is a filename with a grey x on it. When I try to copy it, I get the following message:

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

Yes, it’s complaining at the To Location, not the From Location. I have opened up the directory/files so it’s not read only, but still get this.

Also, getting through these copy errors takes a really long time. If I could avoid trying to copy these files in the first place, it would be much quicker. There’s probably 200 of these files that are archived.

However, I’m copying the folder, not the filenames individually. There are ones that aren’t archived in that folder. There isn’t a plan to clean them up. I’m trying to identify when an error occurs, but it’s only hitting my breakpoint if $error.Exception -ne $null statement after it writes the errors to screen and takes forever failing (see comment).

Any idea how I can either filter out the ones that are archived, or grab them and check them against an array or list so I don’t get an error message? I haven’t figured out how to find them as they happen since it’s copying the entire folder.

I was looking at error checking during copy-item but I don’t see how to apply that to my issue.

This is the copy method:

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)"
    }
}

This is my latest try based on what was suggested by @theo, but it’s trying to copy files I hadn’t tested attributes for a file I can copy, just the dir above it:

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

For example, I’m testing a dir at \drivefolderFormsC Forms and it says it’s a good attribute «16», but there’s a file in that dir that the copy is trying to copy over to my dest dir and I’m seeing it has this for attributes: file.pdf Archive, SparseFile, ReparsePoint, Offline. But I’m not testing that file, the last thing I tested attributes for was the dir it’s in.

I am writing a PowerShell script which uses Copy-Item. I would like to know what kind of exception will be thrown when Copy-Item fails to copy the files from source to destination?

From this link, I don’t see what kind of exception will be thrown.

I want to handle the exception in case of failure.

Ansgar Wiechers's user avatar

asked Sep 22, 2015 at 21:28

n179911's user avatar

Add the tag «-errorAction stop» to the Copy-Item call, within a Try-Catch, like below. The errorAction throws an error which the Try-Catch can handle.

$filepathname = 'valid file path and name'
$dest = 'DoesntExist'

try
{
    Copy-Item $filepathname $dest  -errorAction stop
    Write-Host "Success"
}
catch
{
    Write-Host "Failure"
}

answered Nov 3, 2017 at 17:59

Dan S's user avatar

Dan SDan S

1611 silver badge4 bronze badges

You would want to use a try catch block to capture the error.
With Try/Catch it is important to know that Powershell only catches terminating errors, so you are right to put the -ErrorAction to ‘Stop’.

You don’t need to use $ErrorActionPreference = 'stop' when you already told the command itself to stop on an error.

This will terminate the command with a fatal error if something goes wrong and then trigger your catch statement.

In your catch you can write whatever code you want to if catch is triggered, in the sample below, if an error is triggered we take the current error object, selects the exception message and outputs it to a file.

The Exception object contains a few other properties you could use instead depending on what information you want.

#source1 file location
$source1 = "C:usersmedesktoptest1"

#dest1 file location
$dest1 = "C:usersmedesktopfinal"

#finds files in $source1 that are the very latest and copies to $dest1
try {
Get-ChildItem $source1 -Recurse | Sort-Object -Property CreationTime -Descending | Select-Object -First 1 | Copy-Item -Destination $dest1 -ErrorAction 'Stop'
}
catch{
    $_.Exception.Message | Out-File -FilePath .errorlog.txt -Encoding utf8
}

У меня есть сценарий резервного копирования, который копирует несколько каталогов файлов в папку для резервного копирования. К сожалению, не все файлы в папке доступны. История состоит в том, что они были заархивированы, и что осталось, это имя файла с серым крестиком на нем. Когда я пытаюсь скопировать его, я получаю следующее сообщение:

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 командлет 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”.

copy-item DirectoryExists

Для перезаписи файла с атрибутом read-only, нужно использовать ключ -Force. Если его не использовать, вы получите ошибку “отказано в доступе по пути… CopyFileInfoItemUnauthorizedAccessError”.

copy-item 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

Для примера возьмём такую структуру файлов:

source

Выполним копирование командой:

Copy-Item -Path "C:SourceFolderfol*" -Destination "F:DestFolder"

Результат в F:DestFolder

copy-item replace

Теперь чистим папку назначения и выполняем:

Copy-Item -Path "C:SourceFolderfolder[0-3]" -Destination "F:DestFolder"

Результат:

result

Папка без цифры в окончании не скопировалась, потому что 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

Test-WSMan

Если WSMan не настроен, вы можете выполнить его быструю конфигурацию. Для этого откройте командную строку с правами администратора и выполните
winrm quickconfig

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.

copy-item passthru

Ключ Verbose

При использовании ключа -Verbose вы получите подробный лог операций копирования. Например, вывод команды

Copy-Item -Path "C:SourceFolder*.txt" -Destination "F:DestFolder" -Recurse -Force -Verbose

copy-item 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 также является мощным инструментом для написания скриптов.

В Powershell существует несколько уровней ошибок и несколько способов их обработать. Проблемы одного уровня (Non-Terminating Errors) можно решить с помощью привычных для Powershell команд. Другой уровень ошибок (Terminating Errors) решается с помощью исключений (Exceptions) стандартного, для большинства языков, блока в виде Try, Catch и Finally. 

Как Powershell обрабатывает ошибки

До рассмотрения основных методов посмотрим на теоретическую часть.

Автоматические переменные $Error

В Powershell существует множество переменных, которые создаются автоматически. Одна из таких переменных — $Error хранит в себе все ошибки за текущий сеанс PS. Например так я выведу количество ошибок и их сообщение за весь сеанс:

Get-TestTest
$Error
$Error.Count

Переменная $Error в Powershell

При отсутствии каких либо ошибок мы бы получили пустой ответ, а счетчик будет равняться 0:

Счетчик ошибок с переменной $Error в Powershell

Переменная $Error являет массивом и мы можем по нему пройтись или обратиться по индексу что бы найти нужную ошибку:

$Error[0]

foreach ($item in $Error){$item}

Вывод ошибки по индексу в Powershell c $Error

Свойства объекта $Error

Так же как и все что создается в Powershell переменная $Error так же имеет свойства (дополнительную информацию) и методы. Названия свойств и методов можно увидеть через команду Get-Member:

$Error | Get-Member

Свойства переменной $Error в Powershell

Например, с помощью свойства InvocationInfo, мы можем вывести более структурный отчет об ошибки:

$Error[0].InvocationInfo

Детальная информация об ошибке с $Error в Powershell

Методы объекта $Error

Например мы можем очистить логи ошибок используя clear:

$Error.clear()

Очистка логов об ошибке в Powershell с $Error

Критические ошибки (Terminating Errors)

Критические (завершающие) ошибки останавливают работу скрипта. Например это может быть ошибка в названии командлета или параметра. В следующем примере команда должна была бы вернуть процессы «svchost» дважды, но из-за использования несуществующего параметра ‘—Error’ не выполнится вообще:

'svchost','svchost' | % {Get-Process -Name $PSItem} --Error 

Критические ошибки в Powershell Terminating Errors

Не критические ошибки (Non-Terminating Errors)

Не критические (не завершающие) ошибки не остановят работу скрипта полностью, но могут вывести сообщение об этом. Это могут быть ошибки не в самих командлетах Powershell, а в значениях, которые вы используете. На предыдущем примере мы можем допустить опечатку в названии процессов, но команда все равно продолжит работу:

'svchost111','svchost' | % {Get-Process -Name $PSItem}

Не критические ошибки в Powershell Non-Terminating Errors

Как видно у нас появилась информация о проблеме с первым процессом ‘svchost111’, так как его не существует. Обычный процесс ‘svchost’ он у нас вывелся корректно.

Параметр ErrorVariable

Если вы не хотите использовать автоматическую переменную $Error, то сможете определять свою переменную индивидуально для каждой команды. Эта переменная определяется в параметре ErrorVariable:

'svchost111','svchost' | % {Get-Process -Name $PSItem } -ErrorVariable my_err_var
$my_err_var

Использование ErrorVariable в Powershell

Переменная будет иметь те же свойства, что и автоматическая:

$my_err_var.InvocationInfo

Свойства  ErrorVariable в Powershell

Обработка некритических ошибок

У нас есть два способа определения последующих действий при ‘Non-Terminating Errors’. Это правило можно задать локально и глобально (в рамках сессии). Мы сможем полностью остановить работу скрипта или вообще отменить вывод ошибок.

Приоритет ошибок с $ErrorActionPreference

Еще одна встроенная переменная в Powershell $ErrorActionPreference глобально определяет что должно случится, если у нас появится обычная ошибка. По умолчанию это значение равно ‘Continue’, что значит «вывести информацию об ошибке и продолжить работу»:

$ErrorActionPreference

Определение $ErrorActionPreference в Powershell

Если мы поменяем значение этой переменной на ‘Stop’, то поведение скриптов и команд будет аналогично критичным ошибкам. Вы можете убедиться в этом на прошлом скрипте с неверным именем процесса:

$ErrorActionPreference = 'Stop'
'svchost111','svchost' | % {Get-Process -Name $PSItem}

Определение глобальной переменной $ErrorActionPreference в Powershell

Т.е. скрипт был остановлен в самом начале. Значение переменной будет храниться до момента завершения сессии Powershell. При перезагрузке компьютера, например, вернется значение по умолчанию.

Ниже значение, которые мы можем установить в переменной $ErrorActionPreference:

  • Continue — вывод ошибки и продолжение работы;
  • Inquire — приостановит работу скрипта и спросит о дальнейших действиях;
  • SilentlyContinue — скрипт продолжит свою работу без вывода ошибок;
  • Stop — остановка скрипта при первой ошибке.

Самый частый параметр, который мне приходится использовать — SilentlyContinue:

$ErrorActionPreference = 'SilentlyContinue'
'svchost111','svchost' | % {Get-Process -Name $PSItem}

Игнорирование ошибок в Powershell с ErrorActionPreference и SilentlyContinue

Использование параметра ErrorAction

Переменная $ErrorActionPreference указывает глобальный приоритет, но мы можем определить такую логику в рамках команды с параметром ErrorAction. Этот параметр имеет больший приоритет чем $ErrorActionPreference. В следующем примере, глобальная переменная определяет полную остановку скрипта, а в параметр ErrorAction говорит «не выводить ошибок и продолжить работу»:

$ErrorActionPreference = 'Stop'
'svchost111','svchost' | % {Get-Process -Name $PSItem -ErrorAction 'SilentlyContinue'}

Использование параметра ErrorAction в ошибках с Powershell

Кроме ‘SilentlyContinue’ мы можем указывать те же параметры, что и в переменной $ErrorActionPreference. 

Значение Stop, в обоих случаях, делает ошибку критической.

Обработка критических ошибок и исключений с Try, Catch и Finally

Когда мы ожидаем получить какую-то ошибку и добавить логику нужно использовать Try и Catch. Например, если в вариантах выше мы определяли нужно ли нам отображать ошибку или останавливать скрипт, то теперь сможем изменить выполнение скрипта или команды вообще. Блок Try и Catch работает только с критическими ошибками и в случаях если $ErrorActionPreference или ErrorAction имеют значение Stop.

Например, если с помощью Powershell мы пытаемся подключиться к множеству компьютеров один из них может быть выключен — это приведет к ошибке. Так как эту ситуацию мы можем предвидеть, то мы можем обработать ее. Процесс обработки ошибок называется исключением (Exception).

Синтаксис и логика работы команды следующая:

try {
    # Пытаемся подключиться к компьютеру
}
catch [Имя исключения 1],[Имя исключения 2]{
    # Раз компьютер не доступен, сделать то-то
}
finally {
    # Блок, который выполняется в любом случае последним
}

Блок try мониторит ошибки и если она произойдет, то она добавится в переменную $Error и скрипт перейдет к блоку Catch. Так как ошибки могут быть разные (нет доступа, нет сети, блокирует правило фаервола и т.д.) то мы можем прописывать один блок Try и несколько Catch:

try {
    # Пытаемся подключится
}
catch ['Нет сети']['Блокирует фаервол']{
    # Записываем в файл
}
catch ['Нет прав на подключение']{
    # Подключаемся под другим пользователем
}

Сам блок finally — не обязательный и используется редко. Он выполняется самым последним, после try и catch и не имеет каких-то условий.

Catch для всех типов исключений

Как и было показано выше мы можем использовать блок Catch для конкретного типа ошибок, например при проблемах с доступом. Если в этом месте ничего не указывать — в этом блоке будут обрабатываться все варианты ошибок:

try {
   'svchost111','svchost' | % {Get-Process -Name $PSItem -ErrorAction 'Stop'}
}
catch {
   Write-Host "Какая-то неисправность" -ForegroundColor RED
}

Игнорирование всех ошибок с try и catch в Powershell

Такой подход не рекомендуется использовать часто, так как вы можете пропустить что-то важное.

Мы можем вывести в блоке catch текст ошибки используя $PSItem.Exception:

try {
   'svchost111','svchost' | % {Get-Process -Name $PSItem -ErrorAction 'Stop'}
}
catch {
   Write-Host "Какая-то неисправность" -ForegroundColor RED
   $PSItem.Exception
}

Переменная PSITem в блоке try и catch в Powershell

Переменная $PSItem хранит информацию о текущей ошибке, а глобальная переменная $Error будет хранит информацию обо всех ошибках. Так, например, я выведу одну и ту же информацию:

$Error[0].Exception

Вывод сообщения об ошибке в блоке try и catch в Powershell

Создание отдельных исключений

Что бы обработать отдельную ошибку сначала нужно найти ее имя. Это имя можно увидеть при получении свойств и методов у значения переменной $Error:

$Error[0].Exception | Get-Member

Поиск имени для исключения ошибки в Powershell

Так же сработает и в блоке Catch с $PSItem:

Наименование ошибок для исключений в Powershell

Для вывода только имени можно использовать свойство FullName:

$Error[0].Exception.GetType().FullName

Вывод типа ошибок и их названия в Powershell

Далее, это имя, мы вставляем в блок Catch:

try {
   'svchost111','svchost' | % {Get-Process -Name $PSItem -ErrorAction 'Stop'}
}
catch [Microsoft.PowerShell.Commands.ProcessCommandException]{
   Write-Host "Произошла ошибка" -ForegroundColor RED
   $PSItem.Exception
}

Указываем исключение ошибки в блоке Try Catch Powershell

Так же, как и было описано выше мы можем усложнять эти блоки как угодно указывая множество исключений в одном catch.

Выброс своих исключений

Иногда нужно создать свои собственные исключения. Например мы можем запретить добавлять через какой-то скрипт названия содержащие маленькие буквы или сотрудников без указания возраста и т.д. Способов создать такие ошибки — два и они тоже делятся на критические и обычные.

Выброс с throw

Throw — выбрасывает ошибку, которая останавливает работу скрипта. Этот тип ошибок относится к критическим. Например мы можем указать только текст для дополнительной информации:

$name = 'AD.1'

if ($name -match '.'){
   throw 'Запрещено использовать точки в названиях'
}

Выброс ошибки с throw в Powershell

Если нужно, то мы можем использовать исключения, которые уже были созданы в Powershell:

$name = 'AD.1'

if ($name -like '*.*'){
   throw [System.IO.FileNotFoundException]'Запрещено использовать точки в названиях'
}

Выброс ошибки с throw в Powershell

Использование Write-Error

Команда Write-Error работает так же, как и ключ ErrorAction. Мы можем просто отобразить какую-то ошибку и продолжить выполнение скрипта:

$names = @('CL1', 'AD.1', 'CL3')

foreach ($name in $names){
   if ($name -like '*.*'){
      Write-Error -Message 'Обычная ошибка'
   }
   else{
      $name
   }
}

Использование Write-Error для работы с исключениями в Powershell

При необходимости мы можем использовать параметр ErrorAction. Значения этого параметра были описаны выше. Мы можем указать значение ‘Stop’, что полностью остановит выполнение скрипта:

$names = @('CL1', 'AD.1', 'CL3')

foreach ($name in $names){
   if ($name -like '*.*'){
      Write-Error -Message 'Обычная ошибка' -ErrorAction 'Stop'
   }
   else{
      $name
   }
}

Использование Write-Error и Stop в Powershell

Отличие команды Write-Error с ключом ErrorAction от обычных команд в том, что мы можем указывать исключения в параметре Exception:

Write-Error -Message 'Обычная ошибка' -ErrorAction 'Stop'

Write-Error -Message 'Исключение' -Exception [System.IO.FileNotFoundException] -ErrorAction 'Stop'

Свойства Write-Errror в Powershell

В Exception мы так же можем указывать сообщение. При этом оно будет отображаться в переменной $Error:

Write-Error -Exception [System.IO.FileNotFoundException]'Моё сообщение'

Свойства Write-Errror в Powershell 

Теги:

#powershell

#ошибки

Только вышел из отпуска, и уже аврал на работе, но время для 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?
    Thanks

     Copy-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
     

Понравилась статья? Поделить с друзьями:
  • Powerpoint связанный файл недоступен ошибка диаграмма
  • Powerpoint произошла серьезная ошибка во время последнего запуска
  • Powerpoint ошибка сертификата
  • Powerpoint ошибка при запуске приложения 0xc0000142
  • Powerpoint ошибка печати