A job that blocks your terminal is not inevitable. Ctrl+Z suspends it, bg resumes it in the background, fg brings it back in front of you. Job control is the shell's native mechanism to juggle several processes without opening a new terminal. The behaviours described here were checked with Bash 5.2 on Debian 13, Ubuntu 24.04 and AlmaLinux 10, in a pseudo-terminal.
What you will learn
- Suspend a running job with
Ctrl+Z - List the active jobs with
jobs -l - Move a job to the background with
bg - Bring a job back to the foreground with
fg - Refer to a job by its number with
%n - Kill a job with
kill %n - Detach a job for good with
disown
In which context?
This skill helps as soon as a command takes time and blocks your prompt. A job is a command started from your shell and tracked by it for as long as the session lasts:
- a long compilation or copy,
- an editor you want to put aside,
- a process you started in the foreground by mistake,
- a job you want to survive the terminal closing.
Start a process in the background
The & operator puts the process in the background right from launch. The shell prints the job number and the PID, then gives the prompt back immediately:
sleep 60 &[1] 1842[1] is the job number, local to your shell: it is what fg, bg and kill %1 expect. 1842 is the PID, the identifier of the process for the whole system. The special variable $! holds the PID of the last job started in the background: capture it right away, the next & will overwrite it.
# Capture the PID of the last job started in the backgroundsleep 60 &MYPID=$!echo "PID of the job: $MYPID"List the jobs: jobs
jobs lists the jobs of the current session only, with their
number and state; the -l option adds the PID. In the example below,
sleep 60 runs in the background and vim has been suspended:
jobs -l[1]- 2075842 Running sleep 60 &[2]+ 2075843 Stopped vim file.txtThe current job (+) is the one fg and bg target without an argument:
the last one suspended or started in the background, and Bash gives priority to
a suspended job. Measured with Bash 5.2: a suspended sleep 200 stays
marked + even when a sleep 201 & is started after it. Columns:
| Element | Meaning |
|---|---|
[1] | Job number |
+ | Current job (the one fg resumes by default) |
- | Previous job |
Running / Stopped | State |
sleep 60 & | Command |
Suspend a job: Ctrl+Z
While a command runs in the foreground, Ctrl+Z sends it the suspend signal SIGTSTP and gives the prompt back:
vim file.txt^Z[1]+ Stopped vim file.txtThe process is suspended (state T in ps): it no longer uses the CPU but stays in memory, ready to resume exactly where it stopped. This is what makes Ctrl+Z handy to put an editor aside while you run a command.
Resume in the background: bg
bg (background) makes a suspended job resume in the background: it keeps working, and you keep the prompt. It is the way to rescue a long command started without & by mistake, without losing what has already been done.
bg %1The suspended job [1] resumes running in the background. Without an argument, bg resumes the current job (marked +):
# Suspend then resume in the backgroundsleep 60^ZbgOne limit, measured with cat &: a program that reads the terminal cannot run in the background. It receives the SIGTTIN signal and stops again, and jobs then shows it as Stopped (tty input).
Bring back to the foreground: fg
fg (foreground) gives the foreground back to a job: your terminal is blocked again until it ends, but you regain its interactivity, essential for an editor you put aside.
fg %1fg # resumes the current job (+)fg %2 # resumes job number 2A job that no longer exists answers fg: %2: no such job: it ended in the meantime, or the number is wrong.
Refer to jobs by name
Besides %n, you can name a job by the start of its command, which saves checking jobs before each action. The reference must stay unambiguous: if two jobs start with the same word, Bash refuses to pick one.
fg %vim # resumes the job whose command starts with "vim"kill %sleep # kills the job whose command starts with "sleep"Kill a job by its number
kill accepts a job number preceded by % as well as a PID. Without an option, it sends SIGTERM, the clean stop request; -9 sends SIGKILL, which the program cannot catch.
kill %1 # sends SIGTERM to job 1kill -9 %2 # sends SIGKILL to job 2To clean up every job of the session at once, jobs -p provides only their PIDs, which kill receives in a single line:
kill $(jobs -p)Wait for a job to end: wait
wait blocks the shell until the named process ends, then puts its exit code in $?. It is the synchronisation building block of scripts that start several jobs in parallel.
sleep 3 &MYPID=$!echo "Waiting..."wait $MYPIDecho "Finished with code: $?"Without an argument, wait waits for every active background job of the shell.
Detach a job from the shell: disown
disown removes a job from the job table: the shell will no longer relay
the SIGHUP it receives on disconnection to it, so the job survives the
closing of the session. It accepts a job number (%1) as well as a PID (checked).
sleep 120 &MYPID=$!disown $MYPIDjobs # the job no longer appearsps -p $MYPID # but the process is still aliveA plain exit does not kill a job that is running in the background, by the way:
Bash's huponexit option is disabled by default (checked with
shopt huponexit). The danger comes from the SIGHUP the shell receives when the
SSH connection or the terminal closes: it then relays it to its jobs, and
measured on Debian 13 and AlmaLinux 10, a sleep started with & dies with it.
Two variants cover the other needs: -h protects the job without removing it from the table, which keeps fg possible, and -a handles every job at once.
disown -h $! # protects the job from SIGHUP without removing it from jobsdisown -a # detaches every jobShortcut summary
This table gathers the moves of the page in the order they are used: start, suspend, resume, then detach. Keep it at hand during your first sessions.
| Action | Shortcut / Command |
|---|---|
| Suspend (foreground → stopped) | Ctrl+Z |
| Resume in the background | bg %n |
| Bring back to the foreground | fg %n |
| Start directly in the background | command & |
| Capture the PID of the last background job | $! |
| List the jobs | jobs -l |
| Kill by job number | kill %n |
| Wait for the end | wait $PID |
| Detach from the shell | disown $PID |
Troubleshooting
The symptoms below almost all come from a confusion between the three states of a job (foreground, background, suspended) or between the job number and the PID. Always start with jobs -l, which shows both.
| Symptom | Likely cause | Solution |
|---|---|---|
After bg, the job goes back to Stopped (tty input) | The program reads the terminal: in the background, it receives SIGTTIN and stops (measured with cat &) | Bring it back with fg, or restart it with redirected input (< /dev/null) |
fg %2 answers fg: %2: no such job | The job has ended, or the number is wrong | Check with jobs -l and use the right number |
The job disappears on disconnection despite & | SIGHUP relayed by the shell when the session closes | Use disown -h or nohup |
kill %1 does not end the job | Process in state D or ignoring SIGTERM | kill -9 %1; if state D, diagnose the blocked I/O |
$! empty in the script | No command has been started with & yet | Add & at the end of the command |
Key points
&puts a process in the background at launch;Ctrl+Zsuspends a running process.jobs -llists the jobs with their number and PID;+marks the current job, preferably a suspended one.bg %nresumes,fg %nbrings back,kill %nkills, always by job number.$!holds the PID of the last job started in the background.- An
exitdoes not kill the running jobs; the SIGHUP of a disconnection does. disowndetaches a job already started so that it survives the session closing.
Knowledge check
Check that the essentials of this guide stuck. The questions cover only what has just been explained here.
Knowledge check
Check what you have learned with this interactive quiz
How it works
- The timer starts when you click Start
- Multiple choice, true/false and short answer questions
- You can move between questions
- Detailed results are shown at the end
Starts the quiz and the timer
Review
(0/0)Score by domain
A domain groups several guides and gets several questions, so its score is more reliable than a per-topic score, which often rests on a single question.
Summary
Strengths
No topic passed at 80% on at least two questions yet.
Weak areas
No topic below the pass mark.
Recommended next
Hints before you try again?
Continue the course: Run a command in the background on Linux
A new full quiz with random questions
Work again only on the questions you missed