In this lesson, you will learn how to distinguish whether a command finished its task normally or was abruptly killed by a system signal.
When a process finishes, it returns an integer called an exit status to the shell. You can view this value immediately after any command by checking the special shell variable $?.
Successful Completion vs. Signal Termination
Usually, an exit status of 0 means success. Any non-zero value typically indicates an error. However, a shell script needs to look deeper to understand why a process failed.
If a process finishes on its own, it returns a code defined by the programmer (e.g., 1 for general errors, 2 for syntax errors). But if a process is killed by an external force—like you pressing Ctrl+C or the kernel running out of memory—it terminates via a signal.
When a process is killed by a signal, the shell represents this status as 128 + N, where N is the signal number. For example, if a process is terminated by SIGKILL (signal number 9), the exit status will be 128 + 9 = 137.
Observing the Difference
Consider this example where we force a process to die by a signal:
# Start a process that waits, then kill it from another terminal
sleep 100 &
kill -9 $!
wait $!
echo "Exit status was: $?"
In the example above, the output will be 137. Because the value is greater than 128, your script can programmatically distinguish this from a standard "application error" (which usually falls in the 1–127 range).
By checking if $? is greater than 128, your script can decide whether to retry the operation (in the case of a random signal kill) or report a fatal configuration error (in the case of a standard exit code). Understanding this distinction allows you to write robust monitoring scripts that respond intelligently to process instability versus logical failures.
Key Takeaway: An exit status of 0 indicates success, while a status of 128 plus a signal number indicates the process was forcefully terminated by the system.