none
Не отрабатывает Catch в скрипте powershell RRS feed

  • Вопрос

  • Добрый день! Если скрипт powershell вида:

    try

    { command  -ErrorAction Stop | command | command -ErrorAction Stop      

    command -ErrorAction Stop

    command -ErrorAction Stop

    }

    catch { Write-Output "Error: $PSItem" | Out-File -FilePath .\errlog.txt }

    -

    В начале скрипта я размещаю файл блокировки в целевой директории. По завершении - удаляю.

    Я вижу в логах ошибку:

    Сообщение об ошибке = Выполнение команды остановлено, так как привилегированной переменной

    "ErrorActionPreference" или общему параметру присвоено значение Stop. Не удается найти путь...

    Но при этом не вижу файл блокировки и не вижу содержимого $PSItem. Складывается впечатление, что -ErrorAction Stop в связке с Catch не прерывает работу скрипта при ошибке, а лишь перехватывает. И скрипт продолжает выполняться дальше. Если это так, то почему нет содержимого $PSItem?






    • Изменено Whols 24 февраля 2021 г. 10:00
    24 февраля 2021 г. 8:17

Ответы

  • Благодарю, разобрался! Я видел сообщение об исключении в команде GetChildItem, ошибочно считая, что ранние команды (в том числе запись файла блокировки) выполнены. При отсутствии $ErrorActionPreference = "Stop" скрипт выполнялся дальше (ну, недоступен сетевой путь да и черт с ним! - (с) posh) . Останавливался как раз на нужном мне участке и вызывал исключение, которое следом пишет в лог находящийся (внимание!) на том же сетевом ресурсе (додумался тоже - а что, удобно, все в одном месте и данные и результат работы).

    В итоге, две ошибки в журнале событий и ни одного факта выполнения в директории.

    -

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

    Get-ChildItem "$SrcPath\Свет" -Fi *_base.jpg -R |
           Where-Object {$_.LastWriteTime -GT ((Get-Date).AddDays(-1))} |
                ForEach-Object {Copy-Item -Path ($_.FullName) -D "$Outpath\Свет\" -Force -ErrorAction Stop 
                                }

    Catch { Write-Output "Operation closed with an error: $PSItem" | Out-File -FilePath .\errlog.txt }

    Вот пример рекурсивного копирования с прерыванием работы в случае любой ошибки на этапе копирования.

    -

    Под левым карманом я имел ввиду следующее - если для функции уже предопределена переменная, в которую разработчик заботливо перенес данные, то лучше использовать в этой функции именно ее, а не делать отдельный вызов. К тому же, обработчик Catch сам по себе содержит (может содержать) ряд команд, ошибки в которых приведут к изменению стека $Error. 

    • Помечено в качестве ответа Whols 4 марта 2021 г. 5:52
    25 февраля 2021 г. 12:48

Все ответы

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

    Подход с -ErrorAction "Stop" для каждой команды это не самое правильное решение. Используйте $ErrorActionPreference = "Stop" вначале скрипта или в начале блока Try{}


    The opinion expressed by me is not an official position of Microsoft

    24 февраля 2021 г. 11:17
    Модератор
  • Спасибо за ответ. При написании скрипта я руководствовался этим:

    https://docs.microsoft.com/ru-ru/powershell/scripting/learn/deep-dives/everything-about-exceptions?view=powershell-7.1

    Попробую указать $ErrorActionPreference. Не совсем понимаю, правда, как это скажется на связке Try-Catch - сейчас я задаю список операций через -ErrorAction, любая ошибка в которых, вызывает исключение и запускает обработчик Catch. Если я правильно понял, $ErrorActionPreference = "Stop" указывает, что ВСЕ ошибки должны вызывать остановку скрипта. В этом случае блок Catch, который перехватывает только исключения мне уже не нужен? Что мне даст остановка скрипта в произвольном месте?

    Хочу уточнить, мне нужно понимать, что за ошибка возникла, для этого я пытаюсь читать содержимое $PSItem (как указано в руководстве). Можно, конечно, читать и $Error[0], но это примерно как правой рукой проверять левый карман.

    Через перехват исключения я проверяю три операции (не каждую) - доступность файлов на сетевом ресурсе, доступность FTP, возможность записи в FTP-директорию.

    24 февраля 2021 г. 15:31
  • В общем, в результате эксперементов выяснил, что Catch прекрасно работает в исходном скрипте при возникновении исключения в вызове GetChildItem. Отдает ошибку и вызывает остановку скрипта. Проблемы начинаются при рекурсивном запросе - такой запрос в несуществующую директорию просто...не вызывает никакой ошибки! По крайней мере при ручном выполнении сценария.

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



    • Изменено Whols 24 февраля 2021 г. 16:08
    24 февраля 2021 г. 15:43
  • Мы разговариваем о скрипте которого нигде не видать.

    Я могу что-то предугадывать, но не понимаю почему бы просто не опубликовать то что есть?

    Есть общие рекоммендации по написанию скриптов и $ErrorActionPreference = 'stop' является одной из таких.

    Ошибки бывают терминирующие и не терминирующие, вторые не отлавливаются в Catch блоке.
    Оба варианта и -erroraction которые используете вы и то что предлагаю я меняют поведение ошибок и они начинают прерывать выполнение команды в месте ошибки.

    $error[0], Catch{$_}, Catch{$PSItem} содержат одну и туже ошибку в общем случае, поэтому ваша аналогия с левым карманом мне не ясна


    The opinion expressed by me is not an official position of Microsoft

    24 февраля 2021 г. 16:00
    Модератор
  • Благодарю, разобрался! Я видел сообщение об исключении в команде GetChildItem, ошибочно считая, что ранние команды (в том числе запись файла блокировки) выполнены. При отсутствии $ErrorActionPreference = "Stop" скрипт выполнялся дальше (ну, недоступен сетевой путь да и черт с ним! - (с) posh) . Останавливался как раз на нужном мне участке и вызывал исключение, которое следом пишет в лог находящийся (внимание!) на том же сетевом ресурсе (додумался тоже - а что, удобно, все в одном месте и данные и результат работы).

    В итоге, две ошибки в журнале событий и ни одного факта выполнения в директории.

    -

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

    Get-ChildItem "$SrcPath\Свет" -Fi *_base.jpg -R |
           Where-Object {$_.LastWriteTime -GT ((Get-Date).AddDays(-1))} |
                ForEach-Object {Copy-Item -Path ($_.FullName) -D "$Outpath\Свет\" -Force -ErrorAction Stop 
                                }

    Catch { Write-Output "Operation closed with an error: $PSItem" | Out-File -FilePath .\errlog.txt }

    Вот пример рекурсивного копирования с прерыванием работы в случае любой ошибки на этапе копирования.

    -

    Под левым карманом я имел ввиду следующее - если для функции уже предопределена переменная, в которую разработчик заботливо перенес данные, то лучше использовать в этой функции именно ее, а не делать отдельный вызов. К тому же, обработчик Catch сам по себе содержит (может содержать) ряд команд, ошибки в которых приведут к изменению стека $Error. 

    • Помечено в качестве ответа Whols 4 марта 2021 г. 5:52
    25 февраля 2021 г. 12:48
  •  

    Под левым карманом я имел ввиду следующее - если для функции уже предопределена переменная, в которую разработчик заботливо перенес данные, то лучше использовать в этой функции именно ее, а не делать отдельный вызов. К тому же, обработчик Catch сам по себе содержит (может содержать) ряд команд, ошибки в которых приведут к изменению стека $Error. 

    понятно что написав скрипт криво, при условии что в catch у вас может полететь ексепшн, ваша оригинальная ошибка сместиться с позиции 0 на позицию 1, но и автоматические переменные при неумелом использовании могут менять значения

    так же трай кетчи можно делать вложенными, делать несколько кетчей розделяя их по типам ошибок итд

    пример

    $errorActionPreference = "stop"
    $looperrors = @()
    try {
       # main try
       var1 = 1 # exeption will be here
       gci $env:userProfile -recurse -file | foreach {
           try{
               #secondary try
               # some logic here
               var2 = 2
           } catch {
               $looperrors += $_
           }
        if ($LoopErrors.count -ge 1){
            throw "something was not ok in a loop: $loopErrors"
        }
    } catch {
        #main catch
        write $_
    }


    The opinion expressed by me is not an official position of Microsoft

    25 февраля 2021 г. 13:32
    Модератор