Linux Exit Codes: Evaluating Script Success or Failure
In the Linux operating system, every executed command or script terminates with an integer known as an exit code or return status. Ranging from 0 to 255, these codes serve as a standardized communication mechanism between processes and the operating system. This article explains the vital role exit codes play in signaling whether an operation completed successfully or encountered an error, how the shell captures these signals, and why they are essential for reliable automation.
The Fundamental Rule: Success vs. Failure
The standard convention in Linux assigns specific meaning to numeric exit codes:
- Exit Code
0: Represents success. The command or script executed to completion without unhandled exceptions or critical errors. - Exit Codes
1to255: Represent failure or an abnormal termination. Different non-zero values typically indicate distinct types of errors, allowing calling processes to identify what went wrong.
For example, standard Linux conventions map code 1 to
general errors, 2 to misuse of shell built-ins or syntax
errors, 126 to a command that cannot execute due to
permissions, and 127 to a "command not found" error. Values
above 128 often denote termination caused by fatal OS
signals (such as 130 for a script terminated by
SIGINT via Ctrl+C).
Capturing Exit Codes with
$?
The Linux shell tracks the exit code of the most recently executed
foreground command in the special parameter $?. Inspecting
this variable immediately after a command runs provides instant insight
into its execution status:
ls /existing-directory
echo $? # Outputs 0
ls /non-existent-directory
echo $? # Outputs 2Because $? is overwritten immediately by the next
command, scripts must capture or evaluate it right away.
Core Purposes of Exit Codes
1. Conditional Logic and Flow Control
Exit codes enable automated decision-making in scripts. Shell control
structures such as if, while, and logical
operators evaluate whether a command returns a zero or non-zero status
to determine execution paths:
- Logical AND (
&&): Executes the second command only if the first returns0. - Logical OR (
||): Executes the second command only if the first returns a non-zero code. - If Statements: Branch execution based directly on the command's exit status without requiring explicit manual string parsing.
2. Process Orchestration and CI/CD Pipelines
Automated environments—such as cron jobs, configuration management tools (Ansible, Puppet), and CI/CD pipelines (GitHub Actions, GitLab CI, Jenkins)—rely exclusively on exit codes to monitor build and deployment steps. If a script returns a non-zero code, the pipeline flags the step as failed, halts subsequent dependent jobs, and triggers alerts.
3. Granular Error Diagnostics
Scripts can define custom exit codes using the exit
command (e.g., exit 3 for missing input files,
exit 4 for database connection timeouts). This practice
allows administrators and parent scripts to diagnose failures
programmatically without manually parsing log output.
4. Safe Script Termination
By using shell options such as set -e (errexit), a
script can instruct the shell to abort immediately if any pipeline
returns a non-zero status. This prevents cascading errors from causing
unintended side effects, such as running destructive cleanup routines
after a failed build.