Как получить уровень ошибки команд в конвейере в пакетном программировании Windows? - PullRequest
26 голосов
/ 18 мая 2009

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

Возможно ли как-то вернуть код ошибки предыдущей команды. В частности, возможно ли вернуть код ошибки команды в трубе?

Например, этот однострочный пакетный скрипт

foo.exe

возвращает код ошибки foo . Но вот этот:

foo.exe | tee output.txt

всегда возвращает код выхода tee , который равен нулю.

Ответы [ 6 ]

9 голосов
/ 06 октября 2014

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

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
)
3 голосов
/ 06 октября 2011

Переменная% 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% кажется несовместимым.

Редактировать: Изменен пример для запуска как есть.

3 голосов
/ 18 мая 2009

Один из способов - перенаправить файл.

Как это

foo.exe > tmp.txt
set FOOERR=%ERRORLEVEL%
cat tmp.txt
exit %FOOERR%
2 голосов
/ 28 декабря 2016

Чтобы вызвать tee для ввода bat-файла, а не для одной команды, и свободно использовать errorlevel, я использую трюк, подобный этому:

if "%1" == "body" goto :body
call %0 body | tee log.txt
goto :eof
:body

set nls_lang=american_america
set HomePath=%~dp0

sqlplus "usr/pwd@tnsname" "@%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 от вызова любых команд внутри пакета.

2 голосов
/ 02 января 2010

Примерно через один день копания я нашел способ сделать это:

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, чтобы понять, что он делает :-)). Я не нашел хорошего способа извлечь фактический уровень ошибки. Лучшее, что я получил, это заменил команду 'echo' на какой-нибудь пакетный скрипт call 'call rc.bat', который выглядит так:

@echo %errorlevel%

и затем замените «set error_ = 1» на «set error _ = %% i».

Но проблема в том, что этот вызов тоже может быть неудачным, и это нелегко обнаружить. Тем не менее, это намного лучше, чем ничего - я не нашел никакого решения для этого в Интернете.

1 голос
/ 15 июля 2010

Вы можете решить эту проблему, создав оболочку для вашего командного файла:

rem wrapper for command file, wrapper.cmd

call foo.exe

echo %errorlevel%

if errorlevel 1 goto...

Затем добавьте тройник к обертке:

wrapper.cmd | tee result.log

Конечно, это не совсем то же самое, например, если вы хотите войти в несколько файлов в упакованном файле, это невозможно, но в моем случае это решило проблему.

...