open atlas
↑ Back to track
Command line CLI · 07 · 03

Jobs and backgrounding

Job control lets you suspend, background, and foreground processes within a shell session. nohup and disown protect long-running jobs from SIGHUP when the terminal closes — without them, backgrounding alone is not enough.

CLI Middle ◷ 22 min
Level
FoundationsJuniorMiddleSenior

You start a long-running build, realize you need to do something else in the same terminal, and instinctively reach for Ctrl-C — killing the build entirely. Or you background a job with &, close the terminal, and come back to find the job dead. Both are avoidable. The shell has a full job-control subsystem: suspend, resume, background, foreground, and detach. Once you know those five operations, you stop wasting either your time or your compute.

Goal

After this lesson you can background a new command with &, suspend a running command with Ctrl-Z, inspect jobs with jobs, move jobs between foreground and background with fg and bg, and protect long-running jobs from terminal closure with nohup and disown.

1

& starts a command in the background immediately. Append & to any command and the shell forks the child into a background process group, prints the job number and PID, and returns your prompt immediately.

python train.py &
# [1] 2047

[1] is the job number — a shell-local index. 2047 is the PID. The process is running; your prompt is available. Its stdout and stderr still go to the terminal unless you redirect them.

python train.py > train.log 2>&1 &

Now output goes to train.log and you have a clean terminal. The background process is still in the same session as the shell — it will receive SIGHUP when the session ends (see Step 5).

2

Ctrl-Z sends SIGTSTP to the foreground process group, suspending it. The process is not killed — it is paused (STAT T). The shell regains control and prints:

^Z
[1]+  Stopped                 vim notes.txt

The job is now stopped. Its state in /proc/<PID>/status is T (traced/stopped). It holds all its memory, open files, and sockets — nothing is freed. You can resume it later.

Why this matters: Ctrl-Z is how you escape from an interactive program without losing your work. Suspend vim, run some shell commands, then bring vim back.

3

jobs lists all jobs in the current shell session.

jobs
# [1]-  Stopped    vim notes.txt
# [2]+  Running    python train.py &

The + marks the current job (most recently foregrounded or stopped). The - marks the previous job. You address jobs by number: %1, %2, or %% for the current job.

fg brings a job to the foreground. It reconnects stdin/stdout to the terminal and puts the process back in the foreground process group.

fg %1      # bring job 1 (vim) to foreground
fg         # bring the current job (marked +) to foreground

bg resumes a stopped job in the background. It sends SIGCONT but does not move it to the foreground.

bg %1      # resume job 1 in the background
4

nohup makes a command immune to SIGHUP. When you log out or close a terminal, the shell sends SIGHUP to its entire process group. Any background job that has not been protected will die. nohup redirects stdin from /dev/null, appends stdout+stderr to nohup.out by default, and installs a SIGHUP ignore handler before exec-ing the command.

nohup python train.py > train.log 2>&1 &
echo $!    # PID of the just-launched background job

The process now survives terminal closure. nohup.out is only used if you don’t redirect yourself — always redirect explicitly to avoid litter.

5

disown removes a job from the shell’s job table after the fact. If you forgot to use nohup but the job is already running, disown severs the shell-job relationship. The shell will no longer send SIGHUP to that process when it exits.

./long-script.sh &     # forgot nohup
jobs                   # [1]+  Running  ./long-script.sh
disown %1              # remove from job table
jobs                   # (empty — shell no longer tracks it)

The process is now orphaned from the shell’s perspective. Linux re-parents it to PID 1 (init), which never sends SIGHUP. The process runs until it finishes or is killed explicitly.

disown -h keeps the job in the job table but marks it to not receive SIGHUP — useful if you still want jobs to show it.

Worked example

Run a long backup, check on it mid-way, then protect it from logout.

# Start backup in background with output captured
tar czf /backup/home.tar.gz /home/alice > /tmp/backup.log 2>&1 &
echo "PID: $!"

# Check progress without interrupting
jobs
tail -f /tmp/backup.log   # Ctrl-C to exit tail, backup keeps running

# You realize you need to close this SSH session.
# Protect the job:
disown %%

# Verify it is detached
jobs    # empty
ps aux | grep tar   # still running

If you had used nohup at launch instead:

nohup tar czf /backup/home.tar.gz /home/alice > /tmp/backup.log 2>&1 &

Both approaches work; nohup at launch is simpler. disown saves you when you forgot.

Why this works

Job control (&, fg, bg, jobs) is a feature of interactive shells. In non-interactive scripts (run via cron, systemd, or CI pipelines), job control is disabled by default. Background & still works to fork a process, but fg and bg will fail with “no job control.” For long-running processes in those contexts, use process managers (systemd units, supervisor) rather than shell job control.

Common mistake

Backgrounding a job with & alone does NOT protect it from SIGHUP. This is the single most common job-control mistake. sleep 3600 & then closing the terminal kills the sleep. The background flag only means “don’t block the prompt” — it says nothing about SIGHUP immunity. Always pair & with either nohup (at launch) or disown (after the fact) when you need the job to survive terminal closure.

Check yourself
Quiz

You run: python train.py & then close the terminal. The training process dies. What should you have done?

Recap

& starts a command in the background; Ctrl-Z suspends the foreground process (SIGTSTP, STAT T). jobs lists jobs by number; fg %n brings a job to the foreground; bg %n resumes a stopped job in the background (SIGCONT). Background jobs still receive SIGHUP when the terminal closes — & alone does not protect them. nohup ignores SIGHUP at launch and redirects output to nohup.out; disown removes an already-running job from the shell’s job table, severing the SIGHUP link.

Practice

Start at the top. Tasks go easiest → hardest: recall a fact, apply it to a case, then a senior-level stretch. Open one, attempt it, then reveal.

recallapplystretch0 of 4 done

Something unclear?

Ask a question about this lesson. Questions are anonymous and go straight to the author to make the lesson better.

shortcuts expand
search
K
prev piece
k
next piece
j
cycle tier
t
this menu
?
sources3
expand
  1. 01
  2. 02
  3. 03

Trademarks belong to their respective owners. Editorial reference only.