Learn › Linux Foundations › Chaining
$! - a hands-on Linux lab on a real virtual machine.
Meet $!, the shell variable that remembers the PID of your most recent background job. Fall into the famous ask-too-early trap on purpose, hear the silence, then fix it with a single & and prove the PID landed, all in the practice terminal.
Remember from the job control lessons: end a command with a single & and the shell runs it in the background. You get your prompt back immediately, and the command keeps working out of sight. Which raises a question. Once the prompt is back, how do you find that runner again? It has no window and no name on the screen. It is just gone, somewhere, working.
The shell has you covered. Every time you background a job, the shell writes that job's ID number into a tiny variable called $! (a dollar sign and an exclamation mark, said out loud as "dollar bang"). This lesson is about that one variable: what it holds, when it gets filled, and the famous trap where it answers you with pure silence. You will fall into the trap on purpose, then fix it with a single character.
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. Each box behaves like a freshly opened shell. On the real machine at the end of the module, the chaining mission hands you objectives that use exactly these moves, and you run them for real.
Remember from the $ lesson: every running program has a PID (process ID), a unique number the system uses to tell programs apart, and $ holds the PID of your current shell. $! is its sibling. $! holds the PID of the most recent background job, the last command you started with a single &.
That number is how a script keeps its grip on a job it just launched. Note the PID now, and later you can wait for that exact job to finish, or stop it by number. Without $!, a script that starts a background job has no idea who it just started.
Like $, the value of $! is a live number that is different on every machine and every run. So, just as in the $ lesson, you will never memorize a "right" number here. You will prove facts about the number instead.
The single & is one of the oldest marks in Unix: the very first shell already used it in 1971 to launch background work. When the Bourne shell brought variables to scripts in the late 1970s, it added $! so a script could remember which job it had just launched.
In the $ lesson you used a checker line that proves a varying number is made of digits, without caring which digits. You will lean on it hard today, so run it once to get it back under your fingers.
echo $ | grep -Eq '^[0-9]+#39; && echo "yes: it is all digits"
A quick reminder of the moving parts. echo $ prints your shell's PID. The pipe | feeds that into grep -Eq '^[0-9]+#39;, which succeeds quietly if its input is digits from start to end, and fails quietly if not. Then &&, from the and-operator lesson, prints the message only on success. Before you run it, decide: your PID is different from everyone else's, so can your output still be identical to everyone else's?
prompt: student@linuxcamp:~$ answer: echo $ | grep -Eq '^[0-9]+
#39; && echo "yes: it is all digits" output: yes: it is all digits hint: echo dollar-dollar, a padded pipe, the digit pattern, then && the message: echo $ | grep -Eq '^[0-9]+#39; && echo "yes: it is all digits"Identical output for everyone: yes: it is all digits. The checker collapses a number that varies into a message that never does, which is exactly why it is the safe way to practice on live values. Keep the line in hand. You are about to aim it at $!, and something strange is going to happen.
The practice box below is a freshly opened shell. It has never sent a single command to the background, not one. Take the checker and swap the variable: $! where $ was, nothing else changed.
Before you run it, commit to a prediction: will the message print again, or not?
prompt: student@linuxcamp:~$ answer: echo $! | grep -Eq '^[0-9]+
#39; && echo "yes: it is all digits" output: hint: The exact checker line from the last step, with dollar-bang in place of dollar-dollar: echo $! | grep -Eq '^[0-9]+#39; && echo "yes: it is all digits"Silence. No message, no error, nothing at all. Here is the chain of events. This shell has never backgrounded a job, so $! is empty. echo $! therefore printed an empty line. grep searched it for digits, found none, and failed. And && did what && always does after a failure: it skipped the message. Empty variable in, silence out. That is the trap: $! holds nothing until you background something. Ask too early and every test on it quietly fails.
The silence only happens in a shell that has never backgrounded anything. $! keeps the PID of the last background job until a newer one replaces it. On a machine where you already ran some command with &, even minutes ago, the checker would still find that older job's digits and print the message.
The fix is to hand the shell a background job first. sleep 0.2 is a command that does nothing but nap for a fifth of a second, and a single & after it sends the nap to the background. It is the perfect throwaway job: real enough to get a PID, gone before you notice.
Put the job and the check on one line, so the order can never go wrong: background the nap, then run the digit check on $!. Before you run it, decide: what will $! hold this time, and who put it there?
prompt: student@linuxcamp:~$ answer: sleep 0.2 & echo $! | grep -Eq '^[0-9]+
#39; && echo "yes: it is all digits" output: yes: it is all digits hint: The nap plus a single ampersand, then the checker: sleep 0.2 & echo $! | grep -Eq '^[0-9]+#39; && echo "yes: it is all digits"The message is back. Now look hard at what changed, because the checker half of this line is character for character the one that stayed silent a moment ago. The only new characters are sleep 0.2 & at the front, and they changed everything. The & backgrounded the nap, the shell wrote the nap's PID into $! at that instant, and this time echo $! handed grep real digits. One background job is all it takes to fill $!.
On the real machine, backgrounding a job at an interactive prompt also prints a job line first, something like [1] 2016028. The [1] is a job number, and 2016028 is the PID that $! now holds. A plain echo $! right after would print that same raw number. Both numbers change on every single run, so never expect a specific one. And keep the ampersands straight: a single & backgrounds a job; the double && chains on success.
Take stock, because you just did something most people only do the hard way. The single most common $! mistake in real scripts is reading it before anything was backgrounded, then wondering why a later wait or stop quietly does nothing. Engineers usually meet that bug at midnight, inside someone else's script. You met it on purpose, in a sandbox, watched the silence, and fixed it with one &.
The rule you proved: background first, read $! second. Now the scaffolding comes off.
No command is shown from here on. In one line: start the fifth-of-a-second nap in the background, then prove that the PID it left in $! is a run of digits, using the checker message you know.
prompt: student@linuxcamp:~$ answer: sleep 0.2 & echo $! | grep -Eq '^[0-9]+
#39; && echo "yes: it is all digits" output: yes: it is all digits hint: Job first, check second: the nap with a single ampersand, then echo the variable into the digit grep, then && the message.Job first, read second, message printed. That one line is the whole lesson in miniature: & fills $!, and the checker proves it without ever caring which number landed there.
Still no command shown. A teammate wants proof that your shell itself, not some background job, is running under a numeric ID. Pick the right variable and run the digit check on it. No background job is needed, and that fact is your clue.
prompt: student@linuxcamp:~$ answer: echo $ | grep -Eq '^[0-9]+
#39; && echo "yes: it is all digits" output: yes: it is all digits hint: The shell's own PID lives in the variable from the previous lesson, dollar-dollar, and it is always filled, no ampersand required.$, not $!. That is the pair sorted for good: $ is "which shell am I", always filled, one per session. $! is "what did I background last", empty until the first &, replaced by the next one. Two variables, two questions, and you now reach for the right one on purpose.
You earned this cheat sheet. Every row is a move you made in this lesson.
The one idea underneath it all: $! is the shell's memory of the last job you sent to the background with &. It is empty until the first background job, it changes with every new one, and its value is a live PID you prove facts about rather than memorize.
Remember the code=$? habit from the $? lesson. $! deserves the same respect: the moment you background a job whose ID you will need later, save it with pid=$! on the very next line. The next & overwrites $!, but your saved copy stays put.
You made $! speak and you made it stay silent, and you know exactly why it does each. You backgrounded a job with a single &, watched the shell file its PID into $!, proved the digits with the checker line, and sorted $! from its always-filled sibling $. Every one of those you typed yourself and saw the real result.
The Chaining module ends with one real Linux machine: the chaining mission lab. There a VM boots just for you, and the mission hands you objectives built from this module's operators and variables, with no commands shown. You read the objective, recall the move, and type it. That recall is what turns this practice into a reflex.
Finish the other Chaining lessons, then go put the runner's number to work for real.
Practice The $! Background PID in a real Linux terminal at The Linux Camp. Progress is verified automatically as you type commands on the machine.