LearnLinux FoundationsChaining

The $? Exit Code

$? - a hands-on Linux lab on a real virtual machine.

Read the invisible scoreboard every command leaves behind. Make commands succeed and fail on purpose, read the last exit code with echo $?, learn why it changes after every command, and save it with code=$? before it slips away, all in the practice terminal.

You are back at the shell. Every command you have ever run has quietly handed back a number the moment it finished, a single verdict: did that work, or did it fail? You have never seen it, because the shell keeps it hidden. This lesson is about reading it.

That number is called an exit code, and the shell tucks the most recent one into a special spot named $?. Think of $? as a scoreboard that flips the instant a command ends: 0 means the last command succeeded, and any other number means it failed. In this lesson you will make commands succeed and fail on purpose, then read the scoreboard to see exactly what they reported.

The black boxes below are a practice terminal: a safe sandbox that only checks the one command each step teaches, so you cannot break anything. On the real machine at the end of this module, the chaining mission builds this same fixture and you run these checks for real.

You read the scoreboard by printing it. The command echo writes its argument to the screen, and $? stands in for the last exit code, so echo $? prints the exit code of whatever ran just before it.

To get a known result, run a command whose job is simply to succeed. true is a tiny built-in command that does nothing at all except finish successfully, every single time. Run it, then read the scoreboard:

true; echo $?

The ; between the two commands just means "run this, then run that." So this runs true, and then echo $? prints the code true left behind.

prompt: student@linuxcamp:~$ answer: true; echo $? output: 0 hint: Type the word true, a semicolon, a space, then echo space dollar question-mark: true; echo $?

0. That is the shell's word for success. true finished cleanly, set the scoreboard to 0, and echo $? printed it. From now on, whenever you want to know whether the last thing you ran worked, echo $? is how you ask.

Success is 0. So what does failure look like? For that there is a matching built-in: false, which does nothing except finish by failing, every time. It is the deliberate opposite of true. Run it and read the scoreboard:

false; echo $?

prompt: student@linuxcamp:~$ answer: false; echo $? output: 1 hint: Same shape as before, but with false: false; echo $?

1, not 0. That is the heart of the whole idea: 0 means success, and any non-zero number means failure. It reads backwards from a school grade, where high is good. So it is worth saying out loud once: on the shell, zero is the good score. true gives 0, false gives 1, and now you can tell them apart at a glance.

true and false are training wheels. Real commands set the scoreboard too, based on whether they actually did their job. Your fixture has a file called present.txt that really exists, so listing it succeeds. Watch both the listing and its code:

ls present.txt; echo $?

prompt: student@linuxcamp:~$ answer: ls present.txt; echo $? output: present.txt 0 hint: List the file, semicolon, then read the scoreboard: ls present.txt; echo $?

Two lines came back. First ls printed the name it found, present.txt. Then echo $? printed 0, because ls did its job: the file was there, it listed it, done. A real command reports success exactly the way true did, with a 0.

Now watch a real command fail, on purpose. Your fixture deliberately has no file called missing.txt. Ask ls to list it anyway and it cannot, so it complains and sets a failing code. This one is a read-along, no typing needed. Here is the exact exchange, captured on the lab machine.

ls missing.txt; echo $?

ls: cannot access 'missing.txt': No such file or directory
2

ls printed its error line, and then the scoreboard read 2. Non-zero, so: failure, exactly as the rule promised. The specific number 2 is ls's own way of saying "the path was not there," and different commands pick different non-zero numbers for different troubles. You do not need to memorize them. The one rule that always holds is the one you already know: 0 is success, anything else is failure. Read the number as a yes-or-no first, and only look up its exact meaning if you need the detail.

Here is the catch that trips up everyone at first, and it is the most important thing in this lesson. $? holds only the single most recent exit code. Every command overwrites it, and echo is a command too. So if you read the scoreboard twice in a row, the second read does not report the command you care about. It reports the echo from the first read.

Run false to put a 1 on the board, then read it twice and watch what happens:

false; echo $?; echo $?

prompt: student@linuxcamp:~$ answer: false; echo $?; echo $? output: 1 0 hint: Run false, then read the code twice, each read separated by a semicolon: false; echo $?; echo $?

The first echo $? printed 1, the code from false, just as you would expect. But the second echo $? printed 0. Why? Because the first echo was itself a command, and it succeeded, so it set the scoreboard to 0 before the second read ever looked. You get exactly one clean look at an exit code before the very act of reading replaces it.

Since the scoreboard only holds the latest code, the fix when you need a code more than once is to copy it somewhere safe the instant it appears, before you run anything else. You save it into a name of your own:

false; code=$?; echo "saved: $code"; echo "saved again: $code"

That reads left to right: run false, immediately copy $? into a name called code, then print that saved name as many times as you like. Because code is your own variable, no later command touches it.

prompt: student@linuxcamp:~$ answer: false; code=$?; echo "saved: $code"; echo "saved again: $code" output: saved: 1 saved again: 1 hint: Run false, then code=$? with no spaces around the equals, then echo the saved name twice: false; code=$?; echo "saved: $code"; echo "saved again: $code"

Both lines say 1. You captured false's exit code into code the moment it happened, and now it stays put no matter how many other commands run. This is the everyday habit: the instant a command finishes, if its code matters to you later, do code=$? first, then work with your saved copy instead of the fragile scoreboard. Note the shape, code=$? with no spaces around the =, which is how the shell assigns a variable.

Scaffolding off. No command is shown from here on.

Put both verdicts on the screen in a single line: run the command that always succeeds and read its score, then run the command that always fails and read its score. Four commands, one line, and the screen should show exactly two numbers.

prompt: student@linuxcamp:~$ answer: true; echo $?; false; echo $? output: 0 1 hint: The always-succeed command, a scoreboard read, the always-fail command, another read, with semicolons between all four.

0 then 1, both verdicts in one line. Notice the timing: each read came immediately after the command it was checking, before anything else could overwrite the scoreboard. That read-it-right-away instinct is the core habit of this lesson.

A teammate does not believe the scoreboard overwrites itself. Prove it in one line: put a failing score on the board, then read the scoreboard twice in a row. If you are right, the two reads will disagree.

prompt: student@linuxcamp:~$ answer: false; echo $?; echo $? output: 1 0 hint: The always-fail command first, then the same scoreboard read twice, semicolons between.

First read 1, second read 0. The disagreement is the proof: the first read reported the failure, then its own success set the board to 0 before the second read looked. Your teammate now knows what you know: the scoreboard holds exactly one code, and reading it is itself a command.

Last one, from memory. Run the command that always fails, and this time keep its score: copy it into a name called code the instant it appears. Then print your saved copy twice, once labeled saved: and once labeled saved again:. Both prints must show the failing score.

prompt: student@linuxcamp:~$ answer: false; code=$?; echo "saved: $code"; echo "saved again: $code" output: saved: 1 saved again: 1 hint: Right after the failing command, code=$? with no spaces around the equals, then echo the saved name inside each labeled message.

Both lines say 1, even though two successful echo commands ran in between. Your saved copy in code never flinched while $? itself was overwritten twice. You just drove three drills with no command shown. The scoreboard is yours.

You earned this cheat sheet. Every row is a score you just ran, or watched land, in this lesson:

The one rule that ties it all together: $? is the exit code of the last command. 0 means success, non-zero means failure, and it changes after every single command, echo included. Read it once, right away, or save it with code=$? if you need it again.

You will rarely check $? by hand in real scripts, because the && and || operators you met earlier in this module read it for you and act on it automatically. But every one of those operators is built on the exit code you just learned to read, so this scoreboard is the foundation the rest of the module stands on.

You just read the invisible scoreboard every command leaves behind. true scored 0 and false scored 1. A real ls succeeded on a file that exists, and you watched it fail on one that does not. You saw the $? scoreboard overwrite itself on every read, and learned the code=$? habit that saves a score before it slips away. Then you closed with three challenges driven purely from memory, no command shown.

This module ends with one real Linux machine: the chaining mission lab. There a VM boots just for you on this same fixture, and the mission hands you objectives that use exactly what you just ran, exit codes and $? and the operators built on them. It shows no commands. You read the objective, recall the move, and type it. That recall is what makes it stick.

Finish the other chaining lessons, then go read the scoreboard for real.

Practice The $? Exit Code in a real Linux terminal at The Linux Camp. Progress is verified automatically as you type commands on the machine.