What Is a Pipe? Connecting Commands in the Shell
A pipe, written with the | character, connects the standard output of one command to the standard input of the next, letting you chain tools together.
The pipe is one of the most powerful ideas in the shell: small programs that each do one thing, connected so the output of one becomes the input of the next. CI scripts use pipes constantly, but they also hide a trap, where a failure mid-pipeline can go unnoticed unless you opt into stricter behavior.
What a pipe does
The | operator takes the stdout of the command on its left and feeds it as the stdin of the command on its right. cat log.txt | grep ERROR | wc -l counts error lines by chaining three tools.
Why pipes are useful
Pipes let you compose simple commands into complex behavior without temporary files. Each program stays focused, and you assemble them like building blocks at the command line.
The exit-code trap
- By default a pipeline's exit code is that of the last command only.
- If an earlier command fails but the last succeeds, the pipeline looks fine.
- This can mask real failures inside a CI step.
Fixing it with pipefail
Running set -o pipefail changes the rule: the pipeline fails if any command in it fails. Combined with set -e, this makes a broken stage actually stop the job instead of slipping through.
Pipes in CI
A classic CI bug is generate | tee output.txt where generate crashes but tee succeeds, so the step passes with empty output. Adding set -o pipefail catches exactly this, which is why it belongs in set -euo pipefail.
Reliable pipelines on managed runners
On Latchkey runners, enabling pipefail at the top of a step means a failure anywhere in a pipe fails the job. That turns silent, hard-to-trace bugs into clear, early failures.
Key takeaways
- A pipe connects one command's stdout to the next command's stdin.
- By default only the last command's exit code counts, hiding mid-pipe failures.
set -o pipefailmakes any failing command fail the whole pipeline.