Как получить уровень ошибок команд в трубе в пакетном программировании Windows?
Пакетные файлы по умолчанию возвращают код ошибки последней команды.
Как-то можно вернуть код ошибки прежней команды. В частности, возможно ли вернуть код ошибки команды в трубе?
Например, эта однострочная партия script
foo.exe
возвращает код ошибки foo. Но этот:
foo.exe | tee output.txt
всегда возвращает код выхода tee, который равен нулю.
Ответы
Ответ 1
У меня была аналогичная проблема и я решил следующее решение, так как мне не нужно было точно определять код ошибки только с успехом или неудачей.
echo > .failed.tmp
( foo.exe && del .failed.tmp ) | tee foo.log
if exist .failed.tmp (
del .failed.tmp
exit /b 1
) else (
exit /b 0
)
Ответ 2
Один способ обхода - сделать косвенную передачу через файл.
Подобно этому
foo.exe > tmp.txt
set FOOERR=%ERRORLEVEL%
cat tmp.txt
exit %FOOERR%
Ответ 3
Переменная% ERRORLEVEL% не обновляется до запуска команды pipe; вы должны использовать обертку, как указано в других ответах.
Однако вы можете использовать "IF ERRORLEVEL #". Например:
(
type filename
@REM Use an existing (or not) filename to test each branch
IF ERRORLEVEL 1 (echo ERROR) ELSE (echo OKAY)
) > logfile.txt
ECHO будет работать только в том случае, если была возвращена ошибка; однако% ERRORLEVEL% кажется непоследовательным.
Изменить: Изменен пример, который должен быть запущен как есть.
Ответ 4
Сценарий
foo.exe && set dummy= | tee output.txt
вернет код ошибки foo.exe.
Добавление некоторой фиктивной команды за && сохраняет код завершения команды до && Код выхода фиктивной команды не имеет значения. Команда-пустышка не должна ничего выводить и, предпочтительно, вообще не иметь никакого эффекта. Задание переменной среды в качестве фиктивной команды не имеет никакого эффекта, поскольку конвейерные команды выполняются в собственной оболочке cmd.
Если команда является частью более длинного сценария, тогда полезна следующая строка, поскольку она останавливает выполнение сценария и возвращает правильный код выхода:
foo.exe && set dummy= | tee output.txt || exit /b !errorlevel!
Я проверял это в Windows 7 и Windows 10.
Однако я не нашел никакой документации, объясняющей поведение комбинации && и | Кто-нибудь может объяснить или сослаться на такую документацию?
Ответ 5
Примерно через один день копания я нашел способ сделать это:
set error_=0
9>&1 1>&2 2>&9 (for /f "delims=" %%i in ('9^>^&1 1^>^&2 2^>^&9 ^(^(^(2^>^&1 call "%homeDir%%1"^) ^|^| ^(1^>^&2 2^>nul echo FAILED^)^) ^| 2^>nul "%homeDir%mtee" /T /+ "%homeDir%logs\%date_%_%1.log"^)') do (set error_=1))
exit /b %error_%
В приведенном выше примере выполняется "% homeDir %% 1", а его выход - на "% homeDir% mtee". Эта строка обнаруживает сбои (я бы предложил вам нарисовать диаграмму пакетных контекстов и их назначения stdin/stdout/stderr, чтобы понять, что она делает:-)). Я не нашел хороший способ извлечь фактический уровень ошибок. Самое лучшее, что я получил, это заменить команду "эхо" на некоторый пакетный вызов script call rc.bat, который выглядит так:
@echo %errorlevel%
а затем замените "set error_ = 1" на "set error _ = %% i".
Но проблема в том, что этот вызов также может завершиться неудачей, и обнаружить это непросто. Тем не менее, это намного лучше, чем ничего - я не нашел для этого решения в Интернете.
Ответ 6
Чтобы вызвать tee для ввода bat файла, а не для одной команды, и свободно использовать уровень ошибок, я использую трюк следующим образом:
if "%1" == "body" goto :body
call %0 body | tee log.txt
goto :eof
:body
set nls_lang=american_america
set HomePath=%~dp0
sqlplus "usr/[email protected]" "@%HomePath%script.sql"
if errorlevel 1 goto dberror
rem Here I can do something which is dependent on correct finish of script.sql
:dberror
echo script.sqlerror failed
он отделяет использование tee от вызова любых команд внутри пакета.
Ответ 7
Вы можете решить проблему, создав оболочку вокруг файла команды:
rem wrapper for command file, wrapper.cmd
call foo.exe
echo %errorlevel%
if errorlevel 1 goto...
Затем добавьте tee к обертке:
wrapper.cmd | tee result.log
Конечно, это не совсем то же самое, например. если вы хотите зарегистрировать несколько файлов в завернутом файле, это невозможно, но в моем случае он решил проблему.