13

How do I get the correct return code from a unix command line application after I've piped it through another command that succeeded?

In detail, here's the situation :

$ tar -cEvhf - -I ${sh_tar_inputlist} | gzip -5 -c > ${sh_tar_file}  --  when only the tar command fails $?=0
$ echo $?
0

And, what I'd like to see is:

$ tar -cEvhf - -I ${sh_tar_inputlist} 2>${sh_tar_error_file} | gzip -5 -c > ${sh_tar_file}
$ echo $?
1

Does anyone know how to accomplish this?

2
  • $? holds the error code. You could save it in a variable. Commented Jan 12, 2012 at 10:24
  • 1
    i need to save the return code of the intermediate command , not the last command. Commented Jan 12, 2012 at 10:42

4 Answers 4

10

Use ${PIPESTATUS[0]} to get the exit status of the first command in the pipe.

For details, see http://tldp.org/LDP/abs/html/internalvariables.html#PIPESTATUSREF

See also http://cfajohnson.com/shell/cus-faq-2.html for other approaches if your shell does not support $PIPESTATUS.

Sign up to request clarification or add additional context in comments.

1 Comment

Downvoted, $PIPESTATUS DOES NOT EXIST in POSIX.
4

Look at $PIPESTATUS which is an array variable holding exit statuses. So ${PIPESTATUS[0]} holds the exit status of the first command in the pipe, ${PIPESTATUS[1]} the exit status of the second command, and so on.

For example:

$ tar -cEvhf - -I ${sh_tar_inputlist} | gzip -5 -c > ${sh_tar_file}
$ echo ${PIPESTATUS[0]}

To print out all statuses use:

$ echo ${PIPESTATUS[@]}

4 Comments

Thx, but i don't use bash with my scripts. I work with #!/bin/sh
@dogbane, any difference between ${PIPESTATUS[*]} and ${PIPESTATUS[@]}? I always use the former...
Downvoted, $PIPESTATUS DOES NOT EXIST in POSIX.
For zsh it seems that ${pipestatus[1]} is what holds the code for the first command.
4

Here is a general solution using only POSIX shell and no temporary files:

Starting from the pipeline: foo | bar | baz

exec 4>&1
error_statuses=`((foo || echo "0:$?" >&3) |
        (bar || echo "1:$?" >&3) | 
        (baz || echo "2:$?" >&3)) 3>&1 >&4`
exec 4>&-

$error_statuses contains the status codes of any failed processes, in random order, with indexes to tell which command emitted each status.

# if "bar" failed, output its status:
echo $error_statuses | grep '1:' | cut -d: -f2

# test if all commands succeeded:
test -z "$error_statuses"

# test if the last command succeeded:
echo $error_statuses | grep '2:' >/dev/null

Comments

1

As others have pointed out, some modern shells provide PIPESTATUS to get this info. In classic sh, it's a bit more difficult, and you need to use a fifo:

#!/bin/sh

trap 'rm -rf $TMPDIR' 0
TMPDIR=$( mktemp -d )
mkfifo ${FIFO=$TMPDIR/fifo}

cmd1 > $FIFO &
cmd2 < $FIFO
wait $!
echo The return value of cmd1 is $?

(Well, you don't need to use a fifo. You can have the commands early in the pipe echo a status variable and eval that in the main shell, redirecting file descriptors all over the place and basically bending over backwards to check things, but using a fifo is much, much easier.)

2 Comments

Hello, i've tried this technique but it doesn't seams to work : I tested : /usr/bin/tar -cMEvhf - -I /tmp/test.tmp > $FIFO & gzip -5 -c < $FIFO > /tmp/test.bkz wait $! echo The return value of cmd1 is $?
even when cmd1 fails, the $?=0

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.