5

Have a look at the following commands: why is the value of a not available immediately after the &?

C:\>set a=

C:\>set a=3&echo %a%
%a%

C:\>echo %a%
3

C:\>set a=3&echo %a%
3

But when I do

C:\>set a=

C:\>set a=3&set

a=3 is included in the listed variables!

I need this for a trick I learned here, to get the exit code of a command even output is piped: Windows command interpreter: how to obtain exit code of first piped command but I have to use it in a make script, that's why everything must be in one line! This is what I am trying to do:

target:
    ($(command) & call echo %%^^errorlevel%% ^>$(exitcodefile)) 2>&1 | tee $(logfile) & set /p errorlevel_make=<$(exitcodefile) & exit /B %errorlevel_make%

But errorlevel_make is always empty (the file with the exit code exists and contains the correct exit code).

Is this a bug in cmd? Any ideas what I can do?

Community
  • 1
  • 1
user2452157
  • 313
  • 2
  • 13

3 Answers3

12

The reason for the observed behaviour is how the command line is processed.

In first case set a=3&echo %a%, when the line is interpreted BEFORE EXECUTING IT, all variables are replaced with their values. a has no value until line executes, so it can not be substituted in echo

In second case, set a=3&set, there is no variable substitution before execution. So, when set is executed, a has the value asigned.

MC ND
  • 69,615
  • 8
  • 84
  • 126
5

It's much easier to create a seperate batch file to solve this, as it isn't obvious even for experts.

But in your case this should work

target:
    ($(command) & call echo %^^^^errorlevel% >$(exitcodefile)) 2>&1 | tee $(logfile) & set /p errorlevel_make=<$(exitcodefile) & call exit /B %^errorlevel_make%

A seperate batch could look like

extBatch.bat

@echo off
("%~1" & call echo %%^^errorlevel%% > "%~2") 2>&1 | tee "%~3" & set /p errorlevel_make=<"%~2" 
exit /B %errorlevel_make%

Then you could start the batch from your make file

target:
    extBatch.bat $(command) $(exitcodefile) $(logfile)
jeb
  • 78,592
  • 17
  • 171
  • 225
  • The altered line does not work either, but using a separate batch script is a good idea! I have to figure out how to execute a command given as a command line parameter, or I have to create the batch dynamically. – user2452157 Oct 23 '13 at 16:19
  • Do you get the correct data in your temporary file? At which point it seems to fail? – jeb Oct 23 '13 at 18:53
  • I added some carets to the one line solution, it works in my test-makefile. – jeb Oct 23 '13 at 19:43
  • Yes I did already get the correct data in my temporary file. Thanks to @MC-ND 's answer I also understand why it does not work anyway. Your one line solution still does not work for me, but your batchfile does. Maybe we have different make.exe or OS, I should have mentinon that I use gnu make and win7. But does not matter, I will use a batch file, thanks. – user2452157 Oct 24 '13 at 07:26
0

Inspired by the nice answer of jeb, I modified the code a little to be able to grab errorlevel for each side of the pipe, separately. For example you have the following pipe in your batch file:

CD non_exisitng 2>&1 | FIND /V ""
ECHO Errorlevel @right: %errorlevel%

The errorlevel above refers to the right-hand side of the pipe only, and it indicates if the FIND command succeeded of not. However, to be able to get the errorlevel of both sides, we can change the above code to something like this:

(CD non_exisitng & CALL ECHO %%^^errorlevel%% >err) 2>&1 | FIND /V ""
ECHO Errorlevel @right: %errorlevel%
SET /P err=<err
ECHO Errorlevel @left: %err%

output:

The system cannot find the path specified.
errorlevel @right: 0
errorlevel @left: 1

@OP:

I realize this comes a bit late, but might be helpful for others. In addition to jeb's method of an external batch file, the original problem can also be solved as one-liner:

target:
    ($(command) & call echo %%^^errorlevel%% >$(exitcodefile)) 2>&1 | tee $(logfile) & set /p errorlevel_make=<$(exitcodefile)  & cmd /c call exit /B %%errorlevel_make%%
Amr Ali
  • 3,020
  • 1
  • 16
  • 11