12

Possible Duplicate:
Get exit code of process that's piped to another

I am using the following command line (in a makefile) to pipe the verbose error messages from my compiler through a perl script that simplifies them into something human-readable:

g++ -c source.cpp -o source.o 2>&1 | perl /bin/gSTLFilt.pl

Unfortunately, this approach "masks" the error value returned by the g++ command. make has no idea that the g++ command has failed, because all it gets back is the error result from the perl command.

Is there a way to pipe the output, and still retain the original error condition?

In case it makes a difference: I am using GNU Make 3.81 and g++ (GCC) 3.4.5 (mingw-vista special r3) in an MSYS console running GNU bash, version 2.04.0(1)-release (i686-pc-msys) on Windows XP.

e.James
  • 223

3 Answers3

14

I am not sure what shell sh.exe provides (since there are multiple shells that use that name for their Windows executables), but if it is bash or similar, you can use the $PIPESTATUS array. For your example, you would do:

g++ -c source.cpp -o source.o 2>&1 | perl /bin/gSTLFilt.pl
echo "${PIPESTATUS[0]}"
Chris Down
  • 125,559
  • 25
  • 270
  • 266
  • 1
    That might be working, but wouldn't it just echo the result? I would need it to be the "return value" from that command line for make to recognize it. (sorry if I am not using the correct terminology here). – e.James Dec 01 '11 at 23:21
  • @e.James if (( ${PIPESTATUS[0]} )); then ... command failed ...; else ... command succeeded ...; fi – Chris Down Dec 01 '11 at 23:23
  • Ah. I should have figured that one out on my own. Thank you :) – e.James Dec 01 '11 at 23:25
  • you could use true and false in the if, but I'm not sure this will work because every line is executed in its own subshell (link in my post). – Kevin Dec 01 '11 at 23:26
  • That seems to do the trick. For the record, I used: if (( ${PIPESTATUS[0]} )); then true; else false; fi, and it successfully stops the makefile when an error has occurred. – e.James Dec 01 '11 at 23:38
9

Bash has an option pipefail:

The return status of a pipeline is the exit status of the last command,
unless  the  pipefail  option  is enabled.  If pipefail is enabled, the
pipeline's return status is the value of the last  (rightmost)  command
to  exit  with a non-zero status, or zero if all commands exit success-
fully.

So:

set -o pipefail && $GCC_COMMAND | $PERL_COMMAND

Make executes every line in a subshell for each line, so you need to add it to the beginning of your gcc line. There may be a way to get make to execute just that one command with pipefail set already but I don't know it.

Try adding SHELL=/bin/bash in the Makefile (Make should use this)

Or try:

bash -o pipefail -c "$GCC_COMMAND | $PERL_COMMAND"
Kevin
  • 40,767
6

In traditional shells, the status of the first command in a pipeline is not reported at all to the script. Only the status of the last command is available, in $?.

In bash ≥3.0, when you want to do is stop if an error occurs anywhere in the pipeline, use the pipefail option.

g++ -c source.cpp -o source.o 2>&1 | perl /bin/gSTLFilt.pl

More generally, in bash, the PIPESTATUS array generalizes $? to cover all the commands in the last pipeline.

$ (exit 1) | (exit 2) | (exit 3); echo ${PIPESTATUS[@]}
1 2 3

Zsh has the same feature, only the array is called pipestatus.

% zsh -c '(exit 1) | (exit 2) | (exit 3); echo $pipestatus'    
1 2 3

If you're willing to assume bash (which IIRC is the shell provided by msys as sh), then you can use PIPESTATUS. If you aren't, you can arrange to pass the exit status through to the toplevel shell via the pipe, and make your filter program exit with the status it reads on the last line of input instead of using it as normal input. It's clumsy, but it can be useful.

In makefiles, it's relatively common to use temporary files, here treating the compiler messages as one more intermediate file.

%.otmp %.g++-log: %.cpp
        g++ -c $< -o $@tmp 2>&1 >$*.g++-log
%.o: %.otmp %.g++-log
        perl /bin/gSTLFilt.pl <$*.g++-log
        mv $*.otmp $@
  • Depends what you mean by traditional. (t)csh do report the status of the rightmost failing command in the pipeline, like (k|ba|z)sh -o pipefail (in tcsh, you can disable that with unset anyerror). – Stéphane Chazelas Feb 26 '15 at 12:24