Advanced Bash Scripting

Streams

Pipefail

In this guide, we’ll dive into how Unix-like shells handle pipelines, why errors can be hidden, and how to enforce early exits using set -o pipefail. You’ll learn best practices for robust Bash scripting and see practical examples.

How Pipelines Work

When you connect commands with a pipe (|), each command’s standard output (stdout) feeds into the next command’s standard input (stdin). However, if a middle command writes to standard error (stderr), that error goes straight to your terminal—even though the rest of the pipeline keeps running.

The image illustrates a "Pipe Fail" concept, showing data flow between a computer and processes using standard input (stdin), standard output (stdout), and standard error (stderr).

Behavior Without pipefail

Consider this simple pipeline:

$ sort somefile.txt | uniq | cat file.txt
sort: cannot read somefile.txt: No such file or directory
hello

What happens here:

  • sort fails (exit code ≠ 0) and emits an error.
  • uniq still runs (no input) and exits successfully.
  • cat file.txt prints its content.

Even though sort failed, the pipeline’s final exit status is 0, which masks the error.

Checking Exit Status

Inspect the pipeline’s return code with echo $?:

$ sort somefile.txt | uniq
sort: cannot read somefile.txt: No such file or directory
$ echo $?
0

Despite the failure, you get 0. Likewise, boolean operators behave unexpectedly:

$ sort somefile.txt | uniq && echo "Won't stop on error"
sort: cannot read somefile.txt: No such file or directory
Won't stop on error

Here, echo still runs because the pipeline exit code is 0.

Enabling pipefail

To force a pipeline to return a non-zero status if any command fails, enable pipefail:

#!/usr/bin/env bash
set -o pipefail

sort somefile.txt | uniq && echo "This won't print"
echo "Exit status: $?"

Save as set-pipefail.sh and execute:

$ ./set-pipefail.sh
sort: cannot read somefile.txt: No such file or directory
Exit status: 2

With pipefail:

  • The pipeline returns the exit status of the rightmost failing command.
  • Subsequent commands and && branches are skipped on error.

Common Shell Options

OptionDescriptionDefault
pipefailPipeline fails if any command errorsoff
errexitExit script on any non-zero command (set -e)off
noclobberPrevent overwriting files via redirection (set -C)off

Tip

Stack each set -o on its own line for clarity:

set -o errexit
set -o pipefail
set -o noclobber

Adding a Guard Clause

Combine pipefail with an exit-on-failure guard:

#!/usr/bin/env bash
set -o pipefail

sort somefile.txt | uniq || exit 80

If any pipeline stage fails, the script exits immediately with status 80.

Warning

Always choose a non-zero exit code that makes sense for your script. Avoid overlapping with common system codes.

Combining pipefail with Other Options

Here’s a script that prevents file overwrites and enforces pipeline errors:

#!/usr/bin/env bash
set -o noclobber
set -o pipefail

echo "First line" > file.txt
echo "Second line" > file.txt      # Fails due to noclobber
sort somefile.txt | uniq || exit 100

echo "This line never runs"
exit 0

Run it:

$ ./set-pipefail3.sh
$ cat file.txt
First line

The second redirect fails, and because of pipefail plus the guard clause, the script exits with code 100.

Watch Video

Watch video content

Previous
Pipes