Processes and jobs · bash builtin
jobs
List the background and stopped jobs of the current shell, with their job numbers and state.
jobs [-lnprs] [JOBSPEC...]
A job is a pipeline the shell started: one command, or several joined by |. Ending a line
with & starts it in the background; pressing Ctrl-Z in a terminal stops the foreground job.
jobs lists what this shell is tracking, each with a number in brackets that you use with %:
kill %2, fg %1, bg %3, wait %1. + marks the current job (the default for fg and
bg) and - the previous one.
The job table belongs to one shell. A new terminal, a script, or a subshell has its own, which
is why jobs in a second terminal shows nothing and why a script cannot see the jobs of the
shell that ran it. Processes are still visible to everyone with ps; jobs are a
shell-level bookkeeping layer on top of them.
The examples run in a non-interactive bash -c, where job control is off unless the script
turns it on with set -m. Background jobs and jobs work either way; stopping and resuming
with fg and bg need job control. Ctrl-Z itself needs a terminal; the examples send the
same signal (TSTP) with kill -TSTP instead.
Examples
List background jobs
sleep 300 &
sleep 600 &
jobs
[1]- Running sleep 300 & [2]+ Running sleep 600 &
Two jobs, numbered in the order they were started. [2]+ is the current job and [1]- the
previous one, so a bare fg would pick job 2.
Show the process IDs too
sleep 300 &
sleep 600 | cat &
jobs -l
[1]- 1192 Running sleep 300 &
[2]+ 1193 Running sleep 600
1194 | cat & -l adds the PIDs. A pipeline is one job with several processes, so job 2 lists the PID of
each stage on its own line.
Get just the PIDs for another command
sleep 300 &
sleep 600 &
jobs -p
kill $(jobs -p)
wait
jobs -p | wc -l
1196 1197 1
-p prints one PID per job (the process group leader). kill $(jobs -p) stops all of them;
afterwards there is nothing left to list.
See how finished jobs are reported
sleep 300 &
(sleep 0.1; exit 3) &
true &
sleep 0.3
jobs
echo ---
jobs
[1] Running sleep 300 & [2]- Exit 3 ( sleep 0.1; exit 3 ) [3]+ Done true --- [1]+ Running sleep 300 &
Jobs that ended since the last report are listed once, as Done or Exit N with their
status, and then dropped from the table; the second jobs shows only the running one. An
interactive shell prints these notices before the next prompt.
Filter running and stopped jobs
set -m
sleep 300 &
sleep 600 &
kill -TSTP %1
sleep 0.1
echo "stopped:"; jobs -s
echo "running:"; jobs -r
stopped: [1]+ Stopped sleep 300 running: [2]- Running sleep 600 &
kill -TSTP %1 does what Ctrl-Z does to a foreground job: it stops it. -s lists stopped
jobs only and -r running ones. With job control on, each job is its own process group,
which is what lets the shell stop and resume jobs as units.
Refer to a job by number, name or text
sleep 300 &
python3 -c 'import time; time.sleep(600)' &
jobs %1
jobs %python3
jobs %?600
jobs %-
[1]- Running sleep 300 & [2]+ Running python3 -c 'import time; time.sleep(600)' & [2]+ Running python3 -c 'import time; time.sleep(600)' & [1]- Running sleep 300 &
%N is job N, %name the job whose command starts with name, %?text the one whose
command contains text, and %+/%% and %- the current and previous jobs. Each works
anywhere a job spec is accepted: kill, wait, fg, bg, disown.
Ask for a job that does not exist
sleep 300 &
jobs %3
/bin/bash: line 2: jobs: %3: no such job
Job specs are checked: no such job and exit status 1. Scripts can use `jobs %1
/dev/null 2>&1` to test whether a job is still in the table.
Kill a script's background jobs when it exits
#!/bin/bash
trap 'kill $(jobs -p) 2>/dev/null; wait' EXIT
sleep 300 &
sleep 600 &
jobs -p > helpers.pid
echo "started $(wc -l < helpers.pid) helpers"
bash worker.sh
ps -p "$(paste -sd, helpers.pid)" || echo "helpers are gone"
started 2 helpers
PID TTY TIME CMD
helpers are gone Background jobs outlive the script that started them unless something stops them. The
EXIT trap kills whatever is left in the job table when the script ends, then waits for
it to be gone. Afterwards ps -p finds none of the helper PIDs the script recorded.
Common pitfalls
Expecting
jobsto show processes started in another terminal, by a script, or beforesudo -i. Each shell has its own job table; usepsorpgrepto find processes from elsewhere.Closing the terminal with jobs still running. The shell sends
HUPto its jobs on exit and most programs die from it. Usenohup,disown,setsid, or a terminal multiplexer (tmux, screen) for work that must survive logout.Using job specs in a script started from your interactive shell.
%1inside the script refers to the script's own jobs, not yours.Relying on
jobsoutput format in scripts. It is meant for humans; usejobs -pfor PIDs andwait's exit status for results.
GNU vs BSD and macOS
The examples above ran the GNU/Linux version. These are the differences that matter on macOS and the BSDs (written from their documentation, not run here).
jobsis a POSIX shell builtin, so-land-pwork in bash, zsh, dash and ksh alike.-r,-s,-nand-xare bash extensions (zsh has its own set).
Options worth knowing
A selection, described in our own words. For the full list, see bash(1).
| Option | What it does |
|---|---|
-l | Also show each job's process IDs. |
-p | Print only the PID of each job's process group leader. |
-n | Show only jobs whose state changed since the last report. |
-r | Show only running jobs. |
-s | Show only stopped jobs. |
-x | Run a command with job specs in its arguments replaced by PIDs. |
Related
fgBring a background or stopped job into the foreground, so it gets the terminal and the shell waits for it.bgResume a stopped job in the background, as if it had been started with &.waitWait for background jobs of the current shell to finish, and return their exit status.killSend a signal to processes by PID or job number; by default TERM, which asks a process to exit.nohupRun a command immune to the hangup signal, so it keeps running after you log out or close the terminal.psPrint a snapshot of running processes, with the columns, filters and sort order you choose.- Traps and signalsRunning cleanup code with trap on EXIT, handling INT and TERM, the ERR pseudo-signal, and the timing and scoping rules that trip people up.
- Subshells and command groupingThe difference between ( ... ) and { ...; }, what a subshell inherits and what it cannot change, which constructs create one, and what that costs.