36

Batch files return the error code of the last command by default.

Is it somehow possible to return the error code of a former command. Most notably, is it possible to return the error code of a command in a pipe?

For example, this one-line batch script

foo.exe

returns the error code of foo. But this one:

foo.exe | tee output.txt

always returns the exit code of tee, which is zero.

4
  • 1
    related: stackoverflow.com/questions/11170753/… Commented Jul 7, 2016 at 10:40
  • 3
    Possible duplicate of Windows command interpreter: how to obtain exit code of first piped command Commented Sep 15, 2017 at 16:24
  • What exactly is the goal here? Is it simply to be able to respond to the error code, log the output and display it to the console? If so, mkl probably has the best answer, but I didn't realize that until after I posted mine and reviewed everything one more time. Commented Jan 24, 2018 at 23:18
  • This question and all of the answers here are unworthy of the reps they have accumulated. I've removed my own answer. The best explanation of why the above fails can be found at stackoverflow.com/questions/8192318/…, even though it's not an exact dup of the question. Commented Jan 24, 2018 at 23:25

6 Answers 6

15

I had a similar problem and settled on the following solution as I did not need to detect the exact error code just success or failure.

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
)
Sign up to request clarification or add additional context in comments.

Comments

3

One workaround is to make an indirection through a file.

Like this

foo.exe > tmp.txt
set FOOERR=%ERRORLEVEL%
cat tmp.txt
exit %FOOERR%

1 Comment

This defeats the whole point of using tee which is to get immediate console output as well as being logged to file.
3

The %ERRORLEVEL% variable doesn't get updated before the piped command is run; you have to use a wrapper as advised in the other answers.

However, you can use "IF ERRORLEVEL #". For example:

(
type filename
@REM Use an existing (or not) filename to test each branch
IF ERRORLEVEL 1 (echo ERROR) ELSE (echo OKAY)
) > logfile.txt

The ECHO will only run if an error was returned; however %ERRORLEVEL% seems inconsistent.

Edit: Changed example to be runnable as-is.

4 Comments

This doesn't work for me I get "ECHO was unexpected at this time."
Can the 5 people who upvoted this answer please provide repro steps? I get what @SamMackrill gets. Windows doesn't expect anything at this time.
Suggested answer does not work. It fails with a message "( unexpected at this time" This was observed on windows 10 machine.
Sure does (Win10, in cmd.exe not powershell) with cmd extensions enabled.
2

After about one day of digging, I found a way to do that:

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_%

In the example above "%homeDir%%1" is being executed and its output is piped to "%homeDir%mtee". This line detects failures (I'd suggest you to draw a diagram of batch contexts and their stdin/stdout/stderr assignments in order to understand what it does :-) ). I did not find a good way to extract the actual errorlevel. The best thing I got was to replace the 'echo' command with some batch script call 'call rc.bat' which look like:

@echo %errorlevel%

and then replace 'set error_=1' with 'set error_=%%i'.

But the problem is that this call may fail too, and it is not easy to detect that. Still, it is much better than nothing -- I did not find any solution for that on the Internet.

Comments

2

To call tee for entry bat-file, not for single command, and use errorlevel freely, I use trick like this:

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

it separates using tee from calling any commands inside batch.

Comments

1

You can solve the problem by creating a wrapper around your command file:

rem wrapper for command file, wrapper.cmd

call foo.exe

echo %errorlevel%

if errorlevel 1 goto...

Then append tee to the wrapper:

wrapper.cmd | tee result.log

Of course this does not exactly the same, e.g. if you want to log in several files in the wrapped file, it is not possible, but in my case it solved the problem.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.