Exit codes and command chaining — &&, ||, and ; explained
Every command that finishes running reports back a single number saying whether it succeeded — and that number is the entire mechanism behind '&& only runs if the previous command worked,' not a special case bolted on top.
Beginner
2 min read
Every command reports back a number when it finishes
When any command finishes, it sets an exit code — a number from 0 to
255 that the shell can check. 0 means success. Anything else means some
kind of failure, with the specific non-zero value sometimes indicating
which kind (though that convention varies by program). You can see the exit
code of the last command with a special shell variable:
$ ls /nonexistent-folderls: cannot access '/nonexistent-folder': No such file or directory$ echo $?2
&&, ||, and ; — three ways to chain commands, three different meanings
cmd1 && cmd2 # run cmd2 ONLY IF cmd1 succeeded (exit code 0)cmd1 || cmd2 # run cmd2 ONLY IF cmd1 FAILED (non-zero exit code)cmd1 ; cmd2 # run cmd2 regardless of whether cmd1 succeeded or failed
This is why mkdir new-folder && cd new-folder is a genuinely safer habit
than running the two as separate commands: if mkdir fails (the folder
already exists, or you lack permission), cd never runs, instead of silently
cd-ing into whatever directory you happened to already be in and
continuing as if nothing went wrong.
# Common real patternsnpm run build && npm run deploy # only deploy if the build actually succeededping -c 1 example.com || echo "host unreachable" # fall back if it failsapt update; apt upgrade # run both regardless (less common, riskier)
Exit codes in your own scripts
A shell script can set its own exit code with exit N, which matters
because other tools (CI pipelines, cron, other scripts calling yours) rely
on it to know whether your script actually succeeded:
#!/bin/bashif [ ! -f "config.yml" ]; then echo "Error: config.yml not found" exit 1fiecho "Config found, continuing..."
A script that always exits 0 regardless of what actually happened — even
after printing an error — will report "success" to anything checking its
exit code, silently hiding a real failure from whatever's relying on that
signal.
Why this matters day to day
Command chaining with && is one of the most common patterns in real
scripts and CI pipelines specifically because it enforces "don't proceed
past a failure" without extra if statements — and a CI pipeline stage that
shows green despite an underlying command failing is almost always traceable
back to a script somewhere not propagating its real exit code correctly.