Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

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.

share|improve this question

marked as duplicate by Michael Mrozek Jun 1 '12 at 20:06

This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.

    
Maybe this is off-topic for Unix SE, since it doesn't deal with a Unix environment? –  Chris Down Dec 3 '11 at 16:06
add comment

3 Answers 3

up vote 12 down vote accepted

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]}"
share|improve this answer
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 1 '11 at 23:21
    
@e.James if (( ${PIPESTATUS[0]} )); then ... command failed ...; else ... command succeeded ...; fi –  Chris Down Dec 1 '11 at 23:23
    
Ah. I should have figured that one out on my own. Thank you :) –  e.James Dec 1 '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 1 '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 1 '11 at 23:38
show 1 more comment

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"
share|improve this answer
    
I get: sh: set: pipefail: unknown option name, so it seems that my shell does not support it :( –  e.James Dec 1 '11 at 23:23
    
Too bad, what shell are you using? –  Kevin Dec 1 '11 at 23:24
    
GNU bash, version 2.04.0(1)-release (i686-pc-msys) –  e.James Dec 1 '11 at 23:28
    
Updated with a couple more suggestions. –  Kevin Dec 1 '11 at 23:32
    
I tried set -o pipefail on a command line by itself, and that still gives me the unknown option error. bash is also not available. I may try to install it, but that sounds like a bit more work than I was hoping for :) –  e.James Dec 1 '11 at 23:35
show 1 more comment

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 $@
share|improve this answer
add comment

Not the answer you're looking for? Browse other questions tagged or ask your own question.