How to Get the Exit Status of the First Command in a Pipe

Pipe output and capture exit status in Bash

There is an internal Bash variable called $PIPESTATUS; it’s an array that holds the exit status of each command in your last foreground pipeline of commands.

<command> | tee out.txt ; test ${PIPESTATUS[0]} -eq 0

Or another alternative which also works with other shells (like zsh) would be to enable pipefail:

set -o pipefail
...

The first option does not work with zsh due to a little bit different syntax.

how to get the exit status of the first command in a pipe?

Use the PIPESTATUS array:

$ ls foo | cat
ls: foo: No such file or directory
$ echo ${PIPESTATUS[0]} ${PIPESTATUS[1]}
2 0

Note: PIPESTATUS is a bashism (i.e. not POSIX).

Windows command interpreter: how to obtain exit code of first piped command

You might think you could do something like the following, but it won't work.

(nmake & call set myError=%%errorlevel%%) | tee output.txt

The problem lies in the mechanism by which Windows pipes work. Each side of the pipe is executed in it's own CMD shell. So any environment variable you set there will disappear once the command has finished. Also the delayed expansion of %errorlevel% is more complicated because of the extra level of parsing, and because the CMD shell has a command line context instead of a batch context.

You could do something like this:

(nmake & call echo %%^^errorlevel%% ^>myError.txt) | tee output.txt
for /f %%A in (myError.txt) do echo nmake returned %%A
del myError.txt

Or you could embed the errorlevel in your output.txt:

(nmake & call echo nmakeReturnCode: %%^^errorlevel%%) | tee output.txt
for /f "tokens=2" %%A in ('findstr /b "nmakeReturnCode:" output.txt') do echo nmake returned %%A

Both solutions above assume you are running the commands in a batch script. If you are executing the commands from the command line instead, then both solutions above need

%^^errorlevel% instead of %%^^errorlevel%%.

But given that nmake does not require user input, and it is usually fast so real time monitoring is probably not an issue, then the simplest solution seems to be

nmake >output.txt
set myError=%errorlevel%
type output.txt
echo nmake returned %myError%



Note - there are many subtle complications when working with Windows pipes. A good reference is Why does delayed expansion fail when inside a piped block of code?. I recommend reading the question and all the answers. The selected answer has the best info, but the other answers help provide context.

EDIT 2015-06-02

I've recently discovered you can use DOSKEY macros to cleanly store and retrieve the ERRORLEVEL from either (or both) sides of a pipe, without resorting to a temporary file. I got the idea from DosTips user Ed Dyreen at http://www.dostips.com/forum/viewtopic.php?p=41409#p41409. DOSKEY macros cannot be executed via batch, but the definitions persist after ENDLOCAL and CMD /C exit!

Here is how you would use it in your situation:

(nmake & call doskey /exename=err err=%%^^errorlevel%%) | tee output.txt
for /f "tokens=2 delims==" %%A in ('doskey /m:err') do echo nmake returned %%A

If you want, you can add one more command at the end to clear the definition of the err "macro" after you have retrieved the value.

doskey /exename=err err=

Get exit status of piped commands in PHP

You'll need a little help from the Bash internal array PIPESTATUS. This holds the exit status of each command in the pipe. Since you're looking for the first command's exit status you would be addressing PIPESTATUS[0]. So you're code would look like:

exec(
"bash -c 'mysqldump --user=$u --password=$p --host=$h --port=$p $db | gzip -9 > backup.sql.gz; exit \${PIPESTATUS[0]}'",
$out,
$status
);

Note, this changes the overall exit status of the exec() call and you'll need additional code if you want to catch a failure in a longer chain of commands.

Get exit status of piped commands in Shell

by default the exit status of a pipeline command is the exit status of last command of the pipe.

# returns 0 = SUCCESS
false | true ; echo $?

# returns 1 = FAILED
true | false ; echo $?

So to check the exit status in your case:

echo  'password' | script.sh
status=$?

# example
if [[ $status = 0 ]]; then
echo "SUCCESS"
else
echo "FAILED $status"
fi

Capture stdout to variable and get the exit statuses of foreground pipe

You can:

  1. Use a temporary file to pass PIPESTATUS.

    tmp=$(mktemp)
    out=$(pipeline; echo "${PIPESTATUS[@]}" > "$tmp")
    PIPESTATUS=($(<"$tmp")) # Note: PIPESTATUS is overwritten each command...
    rm "$tmp"
  2. Use a temporary file to pass out.

    tmp=$(mktemp)
    pipeline > "$tmp"
    out=$(<"$tmp"))
    rm "$tmp"
  3. Interleave output with pipestatus. For example reserve the part from last newline character till the end for PIPESTATUS. To preserve original return status I think some temporary variables are needed:

    out=$(pipeline; tmp=("${PIPESTATUS[@]}") ret=$?; echo $'\n' "${tmp[@]}"; exit "$ret"))
    pipestatus=(${out##*$'\n'})
    out="${out%$'\n'*}"
    out="${out%%$'\n'}" # remove trailing newlines like command substitution does

    tested with:

    out=$(false | true | false | echo 123; echo $'\n' "${PIPESTATUS[@]}");
    pipestatus=(${out##*$'\n'});
    out="${out%$'\n'*}"; out="${out%%$'\n'}";
    echo out="$out" PIPESTATUS="${pipestatus[@]}"
    # out=123 PIPESTATUS=1 0 1 0

Notes:

  • Upper case variables by convention should be reserved by exported variables.


Related Topics



Leave a reply



Submit