Игнорирование уровня ошибки!= 0 в Windows PowerShell
У меня есть скрипт, который запускает внешний EXE-файл. Когда этот EXE-файл завершается ошибкой (устанавливает errorlevel равным 1), скрипт PowerShell завершается ошибкой.
Я запускаю curl.exe и получаю это:
- CategoryInfo: NotSpecified: (% Total%... Time Current: String) [], RemoteException + FullyQualifiedErrorId: NativeCommandError
Как я могу игнорировать / отловить сбой внешнего EXE-файла и продолжить работу со своим сценарием?
3 ответа
Это не имеет ничего общего с кодом выхода, возвращаемым EXE. Ошибка генерируется, когда EXE записывает в stderr, но только внутри ISE или при удаленном взаимодействии или использовании фоновых заданий.
EXE-файл, который пишет в stderr, не генерирует ошибки из обычной командной строки PowerShell. Я не уверен, почему это так.
На самом деле приложение работало нормально - Powershell ошибся в сообщении об ошибке.
Когда приложение печатает со стандартной ошибкой, Powershell иногда приходит к выводу о сбое приложения. Это на самом деле дизайнерское решение, принятое разработчиками Powershell. Имхо, это ошибка, потому что многие надежные приложения (такие как curl) печатают полезную информацию со стандартной ошибкой в ходе нормальной работы. В результате Powershell хорошо работает только с другими сценариями Powershell и не может полагаться на взаимодействие с другими приложениями.
Другим читателям в этой теме было трудно воспроизвести поведение, потому что Powershell реализует его непоследовательно. То, происходит ли NativeCommandError, зависит от того, как перенаправляется стандартная ошибка (как следствие, ошибка возникает в vanilla Powershell ISE, но не в vanilla Powershell). Каково бы ни было ваше мнение о проектном решении в первом абзаце, несовместимая реализация наверняка является ошибкой Powershell $LastExitCode=0, но $?=False в PowerShell. Перенаправление stderr в stdout дает NativeCommandError
Я запускал скрипт через PowerShell ISE (IDE), и я считаю, что именно это вызвало проблемы.
Запуск через PowerShell, похоже, работает.
Ошибка, упомянутая в исходном сообщении ("...NativeCommandError"), появляется при перенаправлении stderr на стандартный вывод, например, с помощью "...>file 2>&1" или "*>file". Как упоминалось выше, PowerShell выдает эту ошибку, если что-то выводится через stderr, даже если это "что-то" - просто обычные журналы (без ошибок). "CMake", например, использует stderr как обычный выходной поток трассировки. В то время как старая командная оболочка / сценарий Windows НЕ интерпретирует какой-либо контент на stderr как свидетельство ошибки, PowerShell интерпретирует это, если исполняемый файл (например, CMake) вызывается с помощью "Invoke-Expression" или вызывается напрямую.
Но!... если исполняемый файл вызывается с помощью "Start-Process", эта же проблема НЕ возникает.
Конкретно, случай 1:
Invoke-Expression cmake .... 1>$logFile1 2>$logFile2
ИЛИ
Invoke-Expression cmake .... *>$logFile
Это приводит к указанной выше ошибке
Случай 2:
Start-Process cmake -ArgumentList $arguments ... -RedirectStandardOutput $logFile1 -RedirectStandardError $logFile2
Ошибка не появляется. Файлы "чистые", но два выходных файла заполнены
Заключение: PowerShell ведет себя по-разному в зависимости от того, как вызывается исполняемый файл
Powershell предпочтительно должен вести себя как (старая) командная оболочка Windows, учитывая исполняемые файлы, которые существуют и которые необходимо запускать без возникновения таких проблем. Коды выхода должны быть достаточными для определения ошибок. Если это действительно невозможно, эту функцию PowerShell - интерпретацию чего-либо через stderr как ошибку - следует переключать. И последнее, но не менее важное: противоречивого поведения в этом отношении и среди различных средств вызова исполняемого файла лучше всего избегать.
Если вам не нравится потоковая передача в два отдельных файла, как предлагается в случае "Start-Process", решение, которое работает, - это вызвать исполняемый файл в контексте старой командной оболочки с помощью "cmd / c" в PowerShell. сценарий. Затем вы получаете потоки stdout и stderr в ОДИН ОДИН файл и правильно заполняете (то есть чередуются) обоими потоками по мере работы исполняемого файла. Пример:
cmd /c "cmake ... >$logFile 2>&1"
Поскольку Powershell и Powershell ISE имеют разные способы обработки и обработки вывода ошибок, я обнаружил, что лучший подход в нашей среде - использовать оболочку выполнения команд с использованием старой Windows cmd.exe следующим образом:
$output = $(cmd /c "<<your exe here with parameters>> 2>&1")
Этот метод не требует записи вывода в файл.
После завершения процесса статус ошибки доступен в переменной PowerShell:
$LASTEXITCODE
Пример сценария:
echo top
cmd /c dir foo # doesn't exist
echo bottom
При запуске через ISE, invoke-command, start-job или start-threadjob все, что написано в стандартной ошибке, сгенерирует удаленное исключение. Но скрипт продолжит работу. Это относится только к Powershell 5.
top
Volume in drive C is DISK
Volume Serial Number is 0123-4567
Directory of C:\Users\js
cmd : File Not Found
At C:\Users\js\script.ps1:2 char:1
+ cmd /c dir foo 2>&1
+ ~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (File Not Found:String) [], RemoteException
+ FullyQualifiedErrorId : NativeCommandError
bottom
Один из вариантов - скрыть ошибку с помощью 2>$null
echo top
cmd /c dir foo 2>$null
echo bottom
В результате чего:
top
Volume in drive C is DISK
Volume Serial Number is 0123-4567
Directory of C:\Users\js
bottom