PIPESTATUS

Originally published on 2025-10-20

tl;dr: Bash’s $PIPESTATUS allows you to inspect the exit status of every command in a pipeline.


According to Bash’s manpage, $PIPESTATUS is…

An array variable… containing a list of exit status values from the commands in the most-recently-executed foreground pipeline, which may consist of only a simple command… Bash sets PIPESTATUS after executing multi-element pipelines, timed and negated pipelines, simple commands, subshells created with the ( operator, the [[ and (( compound commands, and after error conditions that result in the shell aborting command execution.

So it’s almost like $?[1] on steroids. Especially useful when using the yes command[2] with interactive commands.

For example:

set -o pipefail
yes | some_interactive_command

If some_interactive_command closes its stdin stream while yes is trying to write to it (which is not uncommon), yes will exit with an exit status of 141. Since we ran set -o pipefail, that will crash the script (even though some_interactive_command may have exited with status 0 “success”).

Let’s modify the script a bit:

set -o pipefail
yes | some_interactive_command || {
  echo "Exited with status $?"
}

This allows the script to keep running, but… sadly it reports the exit status of yes, rather than some_interactive_command.

Exited with status 141

That’s almost never what you want. Nobody cares about yes. We only care about the interactive command.

set -o pipefail
yes | some_interactive_command || {
  echo "Programs in the pipeline exited with these exit statuses: ${PIPESTATUS[*]}"
}

Here we’ll see this output:

Programs in the pipeline exited with these exit statuses: 141 0

So taking it just one step further…

set -o pipefail
yes | some_interactive_command || {
  echo "Exited with status ${PIPESTATUS[1]}"
}

Exited with status 0

That’s more like it. Now we’re looking at the exit status of the command we actually care about. Putting it in a real-world script could look something like this:

set -o pipefail

panic() {
  echo "$*" >&2
  exit 1
}

yes | some_interactive_command || {
  result=${PIPESTATUS[1]}
  test ${result} -eq 0 || panic "some_interactive_command exited with status ${result}"
}

Footnotes

  1. $? expands to the exit status of the most recently used command ↩︎

  2. yes (manpage) is super handy for answering “yes / no” prompts in interactive commands. By default it just writes y to stdout over and over, which is usually enough to get through all those prompts, and effectively makes an interactive command suitable for scripting. ↩︎

Other Posts