Могу ли я заставить объект ошибки PowerShell $ вести себя согласованно как в сценарии, так и в интерактивном режиме?

Я пишу сценарий PowerShell, который использует $error обнаруживать и реагировать на ошибки. Моя проблема в том, что я получаю другое поведение для $error объект в зависимости от того, как я запускаю скрипт. Если я запускаю его в интерактивном режиме (в частности, из PowerShell ISE), то ошибки добавляются в коллекцию, но если я запускаю тот же сценарий из командной строки, возникают те же ошибки, но они не добавляются в коллекцию.

Вот сценарий (урезанный, чтобы проиллюстрировать проблему):

# export_exception_test.ps1

# show me how many errors before we start
"Count = " + $error.Count

try {
    # treat non-terminating errors as terminating errors.
    $ErrorActionPreference = "Stop"
    # echo the setting to output
    $ErrorActionPreference

    # use sqlcmd to save the output of stored procedures to text files.
    # The first and third call will fail because the output folder does not exist.
    # The second call will succeed.
    'first call to sqlcmd'
    sqlcmd -S myserver\myinstance -E -s `"`t`" -Q "EXEC mydatabase.dbo.FI_Codes" -b -o "B:\Exports_new\FI_Codes.txt"
    'second call to sqlcmd'
    sqlcmd -S myserver\myinstance -E -s `"`t`" -Q "EXEC mydatabase.dbo.FI_Codes" -b -o "B:\Exports_newt\FI_Codes.txt"
    'third call to sqlcmd'
    sqlcmd -S myserver\myinstance -E -s `"`t`" -Q "EXEC mydatabase.dbo.FI_Codes" -b -o "B:\Exports_new\FI_Codes.txt"
    # and a whole bunch more of these...

    # The error count should be two more than when we started.
    "Count = " + $error.Count
    # And this should be the most recent error message
    "Message = " + $error[0].Message

}
catch [Exception]
{
    'exception was caught!!!'
    "Count in catch clause = " + $error.Count
    $_.Exception.Message
    # the ultimate goal is to return a non-successful return code when the exports fail
    exit 1
}
finally {
    # set this back to what it was
    $ErrorActionPreference = "Continue"

    # primitive trace output
    'finally.'
}

Когда я запускаю это из PowerShell ISE, он ведет себя как ожидалось. SQLCMD вызывает нескончаемую ошибку, которая перехватывается и обрабатывается. Вот вывод:

PS U:\> C:\Users\etmatt\Documents\PowerShellScripts\export_exception_test.ps1
Count = 0
Stop
first call to sqlcmd
exception was caught!!!
Count in catch clause = 1
Sqlcmd: Error: Error occurred while opening or operating on file B:\Exports_new\FI_Codes.txt (Reason: The system cannot find the path specified).
finally.

Но когда я запускаю его из командной строки, как если бы я настроил запланированное задание, к нему ничего не добавляется $errorи никаких исключений не возникает. Вот вывод:

C:\Users\etmatt>powershell.exe -file "C:\Users\etmatt\Documents\PowerShellScripts\export_exception_test.ps1"
Count = 0
Stop
first call to sqlcmd
Sqlcmd: Error: Error occurred while opening or operating on file B:\Exports_new\FI_Codes.txt (Reason: The system cannot find the path specified).
second call to sqlcmd
third call to sqlcmd
Sqlcmd: Error: Error occurred while opening or operating on file B:\Exports_new\FI_Codes.txt (Reason: The system cannot find the path specified).
Count = 0
Message =
finally.

C:\Users\etmatt>

Я также получаю те же результаты, если запускаю файл сценария из командной строки powershell, а не cmd.exe (что имеет смысл). Например:

PS C:\> ."C:\Users\etmatt\Documents\PowerShellScripts\export_exception_test.ps1"

Я подозреваю, что это как-то связано с контекстом выполнения или, возможно, с режимами синтаксического анализатора, которые я до сих пор не совсем устраиваю, или, может быть, даже с моим профилем (хотя до сих пор я запускал все с одного компьютера под одной учетной записью), Я видел много примеров в Интернете, которые используют этот базовый подход try/catch с $ErrorActionPreference = "Stop"так что, похоже, я должен быть в состоянии сделать эту работу.

Поэтому мой вопрос по сути: могу ли я заставить этот скрипт работать так, как я думаю? А если нет, то что я недопонимаю? Как я могу поймать эти виды ошибок? Если это помогает, мне не нужна сверхдетальная обработка исключений для этого, мне просто нужно знать, когда что-то идет не так, чтобы мой монитор задач мог предупредить меня. Ненулевой код возврата от powershell.exe будет достаточно.

Я смотрю на использование $?, а также $LASTEXITCODE, но я держу пари $? будет иметь ту же проблему, что и $error, и оба применяются только к последнему выполненному выражению, что не идеально, так как мой настоящий сценарий немного длиннее, чем этот пример.

РЕДАКТИРОВАТЬ:
Хорошо, с тех пор я узнал, что исполняемые файлы Windows (например, sqlcmd) никогда ничего не добавляют к $error коллекции, даже если они возвращают ненулевой код завершения. $error используется только командлетами, если я правильно понимаю. Я получил мой сценарий, работающий через многократное использование $LASTEXITCODEхотя я мог бы использовать $? также, потому что он распознает исполняемые коды выхода Windows и получает значение false для ненулевых значений.

Подводя итог, можно сказать, что поведение скрипта при запуске из командной строки является правильным, ожидаемым поведением. Все еще не знаю, почему я получил другие результаты от PowerShell ISE, хотя.

1 ответ

Для меня я вызываю пакетную команду, используя cmd /C <command> и у меня похожая проблема. Для ненулевого кода возврата $? установлен правильно, но нет $error объект (как мог бы знать powershell? так что все в порядке), и код ведет себя аналогично в ISE, а также при выполнении командной строки.

Моя проблема в том, что в таких случаях, когда $ErrorActionPreference = "Stop" это не исключение. Вероятно, потому что это не имеет $error объект бросить. Так что теперь мне нужно проверить $? после каждого вызова другого скрипта или программы, которая не аккуратна. Они должны найти хорошее решение для такой ситуации (возможно, с помощью встроенного исключения, сообщающего, что вызов внешнего сценария / программы не выполнен)

Другие вопросы по тегам