0

I'm trying to use a Windows batch file to run a command, pipe its output to another command, and capture the exit status of the first command. But apparently, I can't do that, although I'll grant that Windows batch files are so arcane that I may have missed a technique. How can I both capture the exit status of a command and pipe its output?

It's the classic do_a_thing | tee logfile.txt usage, but I want to be able to check whether do_a_thing succeeded or not. After lots of fooling around, googling, and reading StackOverflow, I came up with the following:

setlocal enabledelayedexpansion
set rc=-1
( false & set rc=!errorlevel! & echo rc=!rc! ) | tee logfile.txt
echo %rc%

Which I had hoped would produce:

C:\>test.bat 
C:\>setlocal enabledelayedexpansion 
C:\>set rc=-1 
C:\>(false & set rc=!errorlevel!   & echo rc=!rc!  ) | tee logfile.txt
rc=1 
C:\>echo 1
1

But, alas, it did not:

C:\>test.bat
C:\>setlocal enabledelayedexpansion
C:\>set rc=-1
C:\>(false & set rc=!errorlevel! & echo rc=!rc! ) | tee logfile.txt
rc=!rc!
C:\>echo -1
-1

Removing the "| tee logfile.txt" produces the expected value of !rc!, but of course, it doesn't let me capture the log file.

C:\>test.bat
C:\>setlocal enabledelayedexpansion
C:\>set rc=-1
C:\>(false & set rc=!errorlevel! & echo rc=!rc! )
rc=1
C:\>echo 1
1
Ross Patterson
  • 9,527
  • 33
  • 48

1 Answers1

2

What you are trying to to do does not work because each side of the pipe is executed in its own command shell in a command line context with delayed expansion off by default. See Why does delayed expansion fail when inside a piped block of code? for more info. The accepted answer explains everything, and the other answers help give context to the accepted answer.

The easiest solution is to write the error code to a temp file and then read the file into a variable after the command has completed.

( false & call echo %%^^errorlevel%%>returnCode.txt ) | tee logfile.txt
set "rc="
<returnCode.txt set /p "rc="
del returnCode.txt
echo rc=%rc%

The CALL statement provides another level of parsing that occurs after the command has run. The trick is to delay the expansion OF %errorlevel% until after the command you are testing has run. In a command context you can simply use CALL ECHO %^ERRORLEVEL%. But the command line must also pass through a batch context parse before it reaches the command context, so the percents must be escaped as %% and the caret as ^^.

Community
  • 1
  • 1
dbenham
  • 127,446
  • 28
  • 251
  • 390
  • **OK, now that's cool** (or at least as cool as batch files can get). I never knew that CALL could call a batch command like ECHO. All the documentation (_e.g._, [for WinXP](http://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/call.mspx?mfr=true)) says only `.BAT` or `.CMD` files and/or `:labels`. – Ross Patterson Nov 13 '12 at 13:23
  • I knew about the separate shells, and all the implications, and I had previously tried writing `!errorlevel!` to a file and reading it back, but the `call echo %%^^errorlevel%%` was the magic I was looking for. It works beautifully! – Ross Patterson Nov 13 '12 at 13:56