The exit status is the single small integer a program hands back to whoever launched it, reporting whether it succeeded. The GNU Bash Reference Manual’s Exit Status section states the rule that organizes all shell error handling: a command that exits with a zero status has succeeded, while a non-zero status indicates failure. This inverts everyday intuition, where larger numbers feel like more, but it is deliberate, there is one way to succeed and many distinct ways to fail, so zero is reserved for success and the non-zero range carries the reasons.
The range is bounded. The manual notes that exit statuses fall between 0 and 255, and that the shell may treat values above 125 specially. It documents several conventional codes: a command that is not found returns 127, a command found but not executable returns 126, and a command killed by a fatal signal numbered N yields an exit status of 128 plus N. These conventions let a script distinguish a missing program from a crashing one without parsing any text output.
In the shell, the exit status of the most recently run command is available in the special parameter $?. After every command a script can inspect $? to decide what to do next. The shell’s own control flow is built directly on this: an if statement runs its branch based on whether a command returned zero, and the && and || operators chain commands by success or failure. The exit status is therefore not just a report, it is the truth value the shell uses for all of its branching.
This is what makes robust automation possible. A script that ignores exit status will plow ahead after a failed step, often doing damage; a script that checks it can stop, retry, or clean up. The set -e option turns this into a global policy, causing the shell to exit immediately when a command returns non-zero, so a failure halts the script rather than being silently passed over. Combined with the pipefail option for pipelines, set -e is a common foundation for scripts that are meant to fail loudly rather than continue in a broken state.
The convention reaches well beyond interactive shells. Build systems, continuous integration pipelines, and process supervisors all read the exit status of the commands they run to decide whether a step passed. A test runner returns non-zero when tests fail; a compiler returns non-zero when compilation fails; an entire CI pipeline turns red or green on the basis of these integers. The humble return code is the lingua franca by which programs report success up the chain.
Its longevity comes from its minimalism. A single byte, with zero meaning success, is the smallest possible contract between a program and its caller, and it composes cleanly: pipelines, conditionals, and orchestration tools all agree on what it means. That shared meaning, fixed in the Bourne shell and codified by POSIX, is why exit status remains the backbone of error handling across Unix-like systems.