Могу ли я заставить объект ошибки 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
объект бросить. Так что теперь мне нужно проверить $?
после каждого вызова другого скрипта или программы, которая не аккуратна. Они должны найти хорошее решение для такой ситуации (возможно, с помощью встроенного исключения, сообщающего, что вызов внешнего сценария / программы не выполнен)