Stop your subshells from silently eating your exit codes
Technical Briefing | 10/10/2026
You spend all morning refining a complex pipeline, chaining commands with pipes, only to find your script proceeds merrily even when the first process dies with a non-zero exit code. It is one of those silent failures that hides until the pipeline finishes half-baked work. Most people assume the shell handles this out of the box, but unless you tell it otherwise, the exit status of a pipeline is only the exit status of the final command.
Why pipes usually lie to you
When you run something like cat file | grep pattern | sed s/a/b/, the exit code stored in $? reflects the state of the sed command. If cat fails because the file is missing, the pipe just passes an empty stream and the pipeline returns 0. I have debugged countless production jobs that left systems in inconsistent states because of this exact behavior. You need to flip the switch that makes the shell pay attention to every link in that chain.
set -o pipefail
cat data.log | process_data > output.txt || exit 1
Traps for the unwary
- Setting pipefail only affects the pipeline exit status, it does not stop the middle command from finishing.
- Process substitution still needs manual checking since it does not act like a standard pipe.
- Using set -e alongside pipefail will cause your script to abort immediately, which is usually what you actually want.
Just remember that pipefail is a shell option, not a shell variable, so you can toggle it mid-script if you have one specific pipeline that you know will return a non-zero status as a valid condition. Adding that single line to the top of your scripts prevents the most common class of silent data corruption I see in deployment automation. Keep it in your boilerplate and stop chasing phantom successful failures.
