Free — no signup required

Exit Codes: Did it work?

2 min read

The Secret Message ($?)

Every single command executed in Linux finishes by leaving a small integer behind — its exit code (also called exit status). Think of it as a report card: the command tells the shell whether it succeeded or failed.

  • 0: Success. The command completed its task without error.
  • 1–255: Failure. Something went wrong. Different programs use different non-zero values to indicate different types of failure (e.g., grep returns 1 when no match is found, 2 when there is a usage error).

Bash stores the exit code of the most recently executed command in the special variable $?. It has to be read immediately after the command it belongs to — the next command run overwrites it, which is why exit-code checks are always written right next to the command they're checking, never deferred.

In your own scripts, you set the exit code explicitly by calling exit with the value you want the script to report to whatever invoked it — typically 0 on the success path and a nonzero value the moment a precondition fails.

Returning meaningful exit codes from your scripts is what makes them composable. Another script can call yours and use && or || to react to success or failure, just like any other Unix command.

Key Point: The convention of 0=success and non-zero=failure is universal across Unix tools, but the specific non-zero values are program-defined. Always check a tool's man page if you need to distinguish between types of failure (e.g., curl has over 90 distinct error codes). For your own scripts, exit 1 for generic failure is sufficient in most cases.

This is one of 18 chapters

Every chapter is free — Kubernetes, Terraform, SRE, distributed systems, and more — with fast daily review built in.

Sign up free