PowerShell try-catch теряет повторяющиеся ошибки доступа - PullRequest
1 голос
/ 25 октября 2019

Try-catch не надежно фиксирует все ошибки. Try-catch на get-ChildItem не сообщает обо всех ошибках доступа, о которых было сообщено за пределами try-catch.

Редактировать: неверно, что try-catch ненадежен, и есть звукпричина, по которой сообщалось только об одной ошибке. См. Мой комментарий к принятому ответу, чтобы видеть мое недоразумение позади этого вопроса.

В Windows 10 Pro 64, выполняющей PowerShell 5.1, этот сценарий:

$sScriptName  = "ErrorTest.ps1"

write-host ("I will get the file structure of ""C:\Windows\System32"", but this yields two access-denied errors:")

$aoFileSystem = @(get-ChildItem "C:\Windows\System32" -recurse -force)

write-host ("I will now do it again and trap for those errors. But I only get one of them:")

try {$aoFileSystem = @(get-ChildItem $sPath -recurse -force -ErrorAction Stop)}
catch [System.UnauthorizedAccessException]
   {$sErrorMessage = $_.ToString()
    write-host ("System.UnauthorizedAccessException: " + $sErrorMessage.substring(20, $sErrorMessage.length - 32))}

, работающий в ISEкак администратор получает эти выходные данные:

PS C:\WINDOWS\system32> D:\<path>\ErrorTest.ps1
I will get the file structure of "C:\Windows\System32", but this yields two access-denied errors:
get-ChildItem : Access to the path 'C:\Windows\System32\config\systemprofile\AppData\Local\Microsoft\Windows\INetCache\Content.IE5' is denied.
At D:\<path>\ErrorTest.ps1:5 char:19
+ ... aoFileSystem = @(get-ChildItem "C:\Windows\System32" -recurse -force)
+                      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : PermissionDenied: (C:\Windows\Syst...che\Content.IE5:String) [Get-ChildItem], UnauthorizedAccessException
    + FullyQualifiedErrorId : DirUnauthorizedAccessError,Microsoft.PowerShell.Commands.GetChildItemCommand

get-ChildItem : Access to the path 'C:\Windows\System32\LogFiles\WMI\RtBackup' is denied.
At D:\<path>\ErrorTest.ps1:5 char:19
+ ... aoFileSystem = @(get-ChildItem "C:\Windows\System32" -recurse -force)
+                      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : PermissionDenied: (C:\Windows\Syst...es\WMI\RtBackup:String) [Get-ChildItem], UnauthorizedAccessException
    + FullyQualifiedErrorId : DirUnauthorizedAccessError,Microsoft.PowerShell.Commands.GetChildItemCommand

I will now do it again and trap for those errors. But I only get one of them:
System.UnauthorizedAccessException: C:\WINDOWS\system32\config\systemprofile\AppData\Local\Microsoft\Windows\INetCache\Content.IE5

PS C:\WINDOWS\system32> 

Я хочу регистрировать ошибки доступа, но когда я пытаюсь их перехватить, я получаю только первую. Что мне нужно сделать, чтобы второй отобразился, когда я их поймаю?

Редактировать: я получил сообщение о том, что мне нужно отредактировать свой вопрос, чтобы объяснить, чем он отличается от Может ли PowerShell перехватывать ошибки в GetChildItem и продолжать зацикливание? . Поэтому я повторю здесь то, что я сказал в ответ на комментарий ниже от Scepticalist. Этот вопрос для get-ChildItem в цикле ForEach. Моя проблема не связана с такой петлей. Тем не менее, я попробовал что-то из принятого ответа, используя -ErrorAction SilentlyContinue, но это скрывает ошибки, не фиксируя их. Однако в решении, которое я нашел и собираюсь опубликовать в качестве ответа, я использую -ErrorAction SilentlyContinue в сочетании с -ErrorVariable.

Ответы [ 3 ]

1 голос
/ 04 ноября 2019
  • Ошибки, излучаемые Get-ChildItem "C:\Windows\System32" -recurse -force, являются не прекращающимися ошибками, и, учитывая, что такие ошибки по определению не завершают команду,одиночная команда может выдать несколько ошибок этого типа.

    • Все нескончаемые ошибки, генерируемые командлетом, могут быть записаны в назначенную переменную, имя которой вы передаете -ErrorVariableобщий параметр.
    • Кроме того, все ошибки (как не заканчивающиеся, так и завершающие) по умолчанию записываются в глобальную автоматическую переменную сбора $Error сеанса.
  • try / catch действует только на , завершая ошибки, поэтому по умолчанию это не влияет на команды, которые генерируют только не прекращающиеся ошибки, такие как Get-ChildItem "C:\Windows\System32" -recurse -force

    • Вы можете указать команде преобразовать первую не прекращающуюся ошибку в завершающую, добавив -ErrorAction Stop (используя общий параметр -ErrorAction), в этом случае включающий try/ catch делает cисправьте ошибку, но обратите внимание, что выполнение прекращается после того, как ошибка first возникла в любом случае.

Caveat : Есть два типа , завершающие ошибок (и тот факт, что могут быть исторические аварии);в то время как оба типа могут быть перехвачены с помощью try / catch, их поведение по умолчанию отличается:

  • Оператор -определение ошибок, которые завершают только оператор под рукой и после выдачи сообщения об ошибке по умолчанию продолжают выполнение скрипта ;как правило, эти ошибки выдаются (скомпилированными) командлетами, если они сталкиваются с ошибками, которые не ограничиваются вводом под рукой и не позволяют им продолжить обработку дальнейшего ввода.

  • Скрипт -конкретные ошибки (ошибки завершения пространства выполнения), которые вообще прерывают обработку по умолчанию. Код PowerShell, использующий оператор Throw, генерирует такие ошибки, как и передача -ErrorAction Stop команде, которая генерирует нескончаемые ошибки.

    • Предупреждение: в то время как -ErrorAction Stop имеет нет влияет на оператор , определяющий ошибки, , казалось бы, эквивалентная настройка переменной-предпочтения, $ErrorActionPreference = 'Stop', неожиданно повышает оператор , определяющий ошибки к сценарию -конечные.

Дополнительная информация:

0 голосов
/ 25 октября 2019

Я провел некоторое исследование по параметру ErrorAction и, обнаружив параметр ErrorVariable. (Оба являются общими параметрами , поэтому они не отображаются в документации get-ChildItem .) И оттуда я смог выяснить, как правильно перехватывать дляошибки.

Новый скрипт ниже показывает четыре способа сделать это. Методы 2 и 3 работают хорошо. Метод 4 не работает, потому что он неправильно пытается использовать параметр InputObject в ForEach-object. Метод 1, оригинальный метод try-catch, не работает, и я до сих пор не знаю почему. Это беспокоит, потому что важно, чтобы перехват ошибок работал как положено. Если бы я не знал, ожидать двух ошибок, я бы не знал, что try-catch не дает мне правильного вывода.

Я не принимаю этот (мой собственный) ответ, потому что , хотя в приведенном ниже сценарии показаны два метода правильного отслеживания этих ошибок, было бы лучше, если бы кто-то мог объяснитьпочему try-catch не работает для этого.

Новый скрипт:

$sScriptName  = "ErrorTest.ps1"

$sPath = "C:\Windows\System32"

write-host ("I will get the file structure of """ + $sPath + """, but this yields two access-denied errors:")
$aoFileSystem = @(get-ChildItem $sPath -recurse -force)

write-host ("I will now do it again and trap for those errors.")

write-host ("`r`nMethod 1: Original method using try-catch (incorrect results; only finds one of the two errors; why?):")
try {$aoFileSystem = @(get-ChildItem $sPath -recurse -force -ErrorAction Stop)}
catch [System.UnauthorizedAccessException]
   {$sErrorMessage = $_.ToString()
    write-host ("System.UnauthorizedAccessException: " + $sErrorMessage.substring(20, $sErrorMessage.length - 32))}

write-host ("`r`nGet array for Methods 2 to 4.")
$aoFileSystem = @(get-ChildItem $sPath -recurse -force -ErrorAction SilentlyContinue -ErrorVariable aoChildItemError)

write-host ("`r`nMethod 2: Output by piping to ForEach-object (correct results):")
$aoChildItemError | 
    ForEach-object `
       {$oErrorRecord = $_
        write-host ($oErrorRecord.CategoryInfo.reason + ": """ + $oErrorRecord.TargetObject + """")}

write-host ("`r`nMethod 3: Output by for loop (correct results):")
for ($nCount = 0; $nCount -lt $aoChildItemError.count; $nCount++)
   {$oErrorRecord = $aoChildItemError[$nCount]
    write-host ($oErrorRecord.CategoryInfo.reason + ": """ + $oErrorRecord.TargetObject + """")}

write-host ("`r`nMethod 4: Output by ForEach-object loop without pipeline (incorrect results because it incorrectly attempts to use the parameter ""InputObject"" in ""ForEach-object""):")
ForEach-object -InputObject $aoChildItemError `
   {$oErrorRecord = $_
    write-host ($oErrorRecord.CategoryInfo.reason + ": """ + $oErrorRecord.TargetObject + """")}

Новый вывод:

PS C:\WINDOWS\system32> D:\_\z-temp\ErrorTest.ps1
I will get the file structure of "C:\Windows\System32", but this yields two access-denied errors:
get-ChildItem : Access to the path 'C:\Windows\System32\config\systemprofile\AppData\Local\Microsoft\Windows\INetCache\Content.IE5' is denied.
At D:\_\z-temp\ErrorTest.ps1:6 char:19
+ $aoFileSystem = @(get-ChildItem $sPath -recurse -force)
+                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : PermissionDenied: (C:\Windows\Syst...che\Content.IE5:String) [Get-ChildItem], UnauthorizedAccessException
    + FullyQualifiedErrorId : DirUnauthorizedAccessError,Microsoft.PowerShell.Commands.GetChildItemCommand

get-ChildItem : Access to the path 'C:\Windows\System32\LogFiles\WMI\RtBackup' is denied.
At D:\_\z-temp\ErrorTest.ps1:6 char:19
+ $aoFileSystem = @(get-ChildItem $sPath -recurse -force)
+                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : PermissionDenied: (C:\Windows\Syst...es\WMI\RtBackup:String) [Get-ChildItem], UnauthorizedAccessException
    + FullyQualifiedErrorId : DirUnauthorizedAccessError,Microsoft.PowerShell.Commands.GetChildItemCommand

I will now do it again and trap for those errors.

Method 1: Original method using try-catch (incorrect results; only finds one of the two errors; why?):
System.UnauthorizedAccessException: C:\Windows\System32\config\systemprofile\AppData\Local\Microsoft\Windows\INetCache\Content.IE5

Get array for Methods 2 to 4.

Method 2: Output by piping to ForEach-object (correct results):
UnauthorizedAccessException: "C:\Windows\System32\config\systemprofile\AppData\Local\Microsoft\Windows\INetCache\Content.IE5"
UnauthorizedAccessException: "C:\Windows\System32\LogFiles\WMI\RtBackup"

Method 3: Output by for loop (correct results):
UnauthorizedAccessException: "C:\Windows\System32\config\systemprofile\AppData\Local\Microsoft\Windows\INetCache\Content.IE5"
UnauthorizedAccessException: "C:\Windows\System32\LogFiles\WMI\RtBackup"

Method 4: Output by ForEach-object loop without pipeline (incorrect results because it incorrectly attempts to use the parameter "InputObject" in "ForEach-object"):

UnauthorizedAccessException UnauthorizedAccessException : " C:\Windows\System32\config\systemprofile\AppData\Local\Microsoft\Windows\INetCache\Content.IE5 C:\Window
s\System32\LogFiles\WMI\RtBackup "

PS C:\WINDOWS\system32>  
0 голосов
/ 25 октября 2019

Довольно просто! Вы указали параметр -ErrorAction Stop во втором операторе доступа к каталогу. Переключите его на -ErrorAction Continue , и вы получите оба сообщения об ошибке.

...