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.

I would like to make a pipeline of Bash scripts like this

prog1 | prog2

such that prog2 can see the exit code of prog1 and act differently based on that information.

Is this possible?

share|improve this question
    
Could you elaborate the act differently part of your question? –  devnull Mar 7 at 16:21
    
You have limited control over how far prog2 has progressed when prog1 exits, due to the internal buffering used to implement the pipe and how prog1 and prog2 are scheduled. –  chepner Mar 7 at 18:04
    
Also look at [In what order do piped commands run?](unix.stackexchange.com/q/37508) –  DK Bose Mar 8 at 2:12

5 Answers 5

up vote 4 down vote accepted

The general answer is no. It's possible for prog2 to exit before prog1 even starts (obviously that can't happen if prog2 actually reads some input, which you'd expect it to do if you're using it in a pipeline). It's definitely possible for prog2 to exit before prog1; this happens for example when prog2 is a search program that exits as soon as it finds a match, in which case prog1 may not have finished producing all the data yet.

There is no direct way for prog2 to retrieve the exit status of prog1 or even to know that prog1 has exited. All that prog2 can know is that prog1 has closed its end of the pipe, which it can do without dying.

If you want to obtain the exit status of prog1 from prog2, there are two common methods: you can write it to a file, or you can send it through the pipe. Sending the output status as the last line of the piped data is a possibility. You have to make sure not to process the last line until you know that it's the last line, i.e. until you've tried to read the next line.

{ prog1; echo $?; } | …

Here's an example where the right-hand side is a text filter that colors every line containing the word “error” in red. If the left-hand side fails, the right-hand side exits with the same status.

{ prog1; echo $?; } | awk '
    NR != 1 {
        if (line ~ /[Ee][Rr][Rr][Oo][Rr]/) print "\033[31m" line "\033[0m";
        else print line;
    }
    {line = $0}
    END {exit($0)}
'
share|improve this answer
    
I was trying { command; echo ${PIPESTATUS[@]}; } | sort | ... so that the exit status comes first in the stream. It's all very interesting! –  Amphiteóth Mar 8 at 0:53
    
@illuminÉ That would only work if ${PIPESTATUS[@]} is sorted before anything else in the output of command. If command prints out a bunch of numbers, or if it can print out arbitrary text, you're in trouble: you won't be able to distinguish its output from the status line. –  Gilles Mar 8 at 1:10
    
Thank you, indeed it only succeeds at sorting a success status to top if the command doesn't contain 0 in its output lol. Tgif/s. –  Amphiteóth Mar 8 at 1:33

Although you can in some special cases (see the other answers) you can't in every case. Some filter programs will just keep going, while others will hold all output, unleash it in a single blast, and then exit.

For an example of a "just keep going" program, grep will server, as would tail -f /var/log/some_log_file. Using sort in a pipeline causes a "stall", as sort will collect input until the pipe in front of it closes. Using xargs adds a further complication: are the programs started by xargs (it might start many instances) part of the pipeline or not?

share|improve this answer
    
-1 because you where upvoted for rationalising an incorrect answer. –  richard Mar 7 at 18:19
    
@richard Huh? Bruce's answer is correct (although the second paragraph is a little confusing). –  Gilles Mar 7 at 23:45

The answer: Not directly.

@terdon has illustrated that the exit code of the previous command in the pipe must be sent as an explicit parameter to the next command.

Remember that the pipe is merely a mapping of the previous command's STDOUT to the next command's STDIN; exit codes are not outputted to STDOUT (or STDERR).

share|improve this answer
1  
-1 because you where upvoted for quoting an incorrect answer, and not saying much more. –  richard Mar 7 at 18:18
    
@richard fair enough... I should've double checked... that's what happened if I force myself to answer a question when very tired... –  pepoluan Mar 7 at 18:35

All process, in pipeline, are started before any exit. Therefore prog2 could have to get this info after it had started, it would also have to hold off processing until prog1 had exited, this could stall the pipe. There seems to be fundamental problems in doing what you ask, not OS limitations.

You probably need to consider a temporary file, or putting the result in a variable.

Example for small amount of data, using a variable.

tmp=$(prog1)
if test "z$PIPESTATUS" == "z0"
then
   …
else
   …
fi
share|improve this answer
    
There's a gap in your reasoning. prog2 is started before prog1 completes in general, but there could be a way for it to receive the output status from prog1 while it's running. –  Gilles Mar 7 at 23:46

To finish Gilles’s answer,

(prog1; echo $? > /tmp/prog1.status) | prog2

is an approach.  prog2 could either

  • read standard input to the end, and then read /tmp/prog1.status, or
  • check for the existence of /tmp/prog1.status periodically while reading the standard input.
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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