Get exit status of process that's piped to another

  • I have two processes foo and bar, connected with a pipe:

    $ foo | bar
    

    bar always exits 0; I'm interested in the exit code of foo. Is there any way to get at it?

  • camh

    camh Correct answer

    9 years ago

    If you are using bash, you can use the PIPESTATUS array variable to get the exit status of each element of the pipeline.

    $ false | true
    $ echo "${PIPESTATUS[0]} ${PIPESTATUS[1]}"
    1 0
    

    If you are using zsh, they array is called pipestatus (case matters!) and the array indices start at one:

    $ false | true
    $ echo "${pipestatus[1]} ${pipestatus[2]}"
    1 0
    

    To combine them within a function in a manner that doesn't lose the values:

    $ false | true
    $ retval_bash="${PIPESTATUS[0]}" retval_zsh="${pipestatus[1]}" retval_final=$?
    $ echo $retval_bash $retval_zsh $retval_final
    1 0
    

    Run the above in bash or zsh and you'll get the same results; only one of retval_bash and retval_zsh will be set. The other will be blank. This would allow a function to end with return $retval_bash $retval_zsh (note the lack of quotes!).

    And `pipestatus` in zsh. Unfortunately other shells don't have this feature.

    Note: Arrays in zsh begin counterintuitively at index 1, so it's `echo "$pipestatus[1]" "$pipestatus[2]"`.

    You could check the whole pipeline like this: `if [ \`echo "${PIPESTATUS[@]}" | tr -s ' ' + | bc\` -ne 0 ]; then echo FAIL; fi`

    There is no $PIPESTATUS in POSIX. Downvoted.

    @JanHudec: Perhaps you should read the first five words of my answer. Also kindly point out where the question requested a POSIX-only answer.

    @camh: I did. I don't care. The question is not tagged bash, so the answer is incomplete.

    @JanHudec: Nor was it tagged POSIX. Why do you assume the answer must be POSIX? It was not specified so I provided a qualified answer. There is nothing incorrect about my answer, plus there are multiple answers to address other cases.

    @camh: In that case correct answer should work all the way back to Bourne Shell!

    I edited this answer to include the `zsh` equivalent as well as a version that will work in either `zsh` or `bash`. Thanks, this is far preferable to the POSIX file descriptor hack, (at least for my rc files).

    You can capture all the status values in one go with an array assignment - `false | true; r=("${PIPESTATUS[@]}")` and then `echo "> ${r[0]} - ${r[1]} <"`

    Note that the `false | true` example may mislead; `${PIPESTATUS[0]}` is the result of the first command (`false`) of which the output is `1`. This can be proven by; `false | echo $?` (outputs `1`). IOW, If you're scripting, and have a command with pipes, use `${PIPESTATUS[0]}` if you want the result of the very first command, `${PIPESTATUS[1]}` for the result of the second command (i.e. after the first '|' pipe) and so on.

License under CC-BY-SA with attribution


Content dated before 6/26/2020 9:53 AM