Getting Started with Job Control - jobs, fg, bg, and Ctrl+Z
What You'll Learn
- Use
Ctrl+Zto pause a running command and reclaim your terminal - Swap freely between foreground and background with
fg/bg - List current jobs with
jobsand target individual jobs with%n - Run a command in the background from the start by appending
& - Get past common gotchas like "I lost my work" or "this won't go away"
Target Audience: Anyone who's been editing a file in vim or running a long command and wanted to "just check one thing" in the same terminal — and accidentally killed everything with Ctrl+C.
Introduction: The Day Lina Killed Her Vim Session
vim, and I needed to run ls in another terminal. I didn't know how to open a new one, so I just hit Ctrl+C — and it killed the whole vim session. I lost every unsaved change.Ctrl+Z (Control-Z).Ctrl+C kills the running command, but Ctrl+Z pauses it and sets it aside. Your terminal comes back so you can run other commands, and when you're done you call fg to return to the paused work.Ctrl+Z (pause), bg (resume in background), fg (return to foreground), jobs (list).The Practical Pattern
- Want your terminal back? →
Ctrl+Z→bg(keeps running in the background) - Want to pause and leave it alone? →
Ctrl+Zby itself (stays paused) - Want to come back to it? →
fg - Forgot what's running? →
jobs
Assumed Environment
- bash / zsh (job control is a standard POSIX shell feature)
- Does not work in
dashor non-interactive shells (/bin/sh -c) - All keystroke examples assume an interactive terminal session
1. "Job" vs. "Process" — What's the Difference?
ls | grep .txt | sort, the OS creates three processes, but the shell treats it as one job. When you hit Ctrl+Z, the shell pauses the whole job — all three processes — at once.Job Number vs. PID
| Type | Example | Assigned by | Used for |
|---|---|---|---|
| Job number | %1 |
The shell | fg %1, kill %1, etc. |
| PID | 12345 |
The kernel | kill 12345, ps output |
Memory trick: Job numbers always take a leading %. It's fg %1 (not fg 1), kill %1 (not kill 1). The latter — kill 1 — sends the kill signal to PID 1 (init), which is a dangerous command if you have permission.
2. Ctrl+Z: Pause Right Now
vim, tail -f, python interactive — anything that holds your terminal — then press Ctrl+Z.fg thaws it out and resumes as if nothing happened.Try It
$ sleep 100
sleep 100 does nothing for 100 seconds. Your terminal is locked the whole time.
Now press Ctrl+Z.
^Z [1]+ Stopped sleep 100 $
If you see [1]+ Stopped, it worked. Notice the $ prompt is back — the terminal is yours again.
Reading [1]+
[1]= job number 1+= the most recently touched job (the default target forfg/bg)Stopped= paused (consuming no CPU)
You Can Now Run Other Commands
$ ls $ pwd $ echo "free to do other things"
sleep stays frozen in the background while you do whatever you want. That's the magic of Ctrl+Z.
3. fg: Bring Back / bg: Resume Behind
fg = bring it back to the front and resume. bg = resume in the background while you keep using the terminal. The first puts your attention back on the job; the second keeps you free for other work.fg: Back to Foreground
Let's bring back the sleep 100 from before.
$ fg
sleep 100
sleep continues from where it left off, and the terminal is locked again. Wait it out, hit Ctrl+Z again, or Ctrl+C to kill it.
bg: Resume in Background
If you want it running but don't want to wait for it:
$ sleep 100 ^Z [1]+ Stopped sleep 100 $ bg [1]+ sleep 100 & $
It moves from Stopped to & (running in background). sleep keeps ticking while you do other things.
Not all commands work with bg: Interactive full-screen apps like vim can't "wait for input in the background," so bg-ing them just sends them back to a stopped state (Stopped (tty input)). For vim, the right rhythm is Ctrl+Z to pause → fg to return — don't try to bg it.
4. Append &: Background From the Start
& when you launch it.Ctrl+Z → bg in two steps, do it in one?Ctrl+Z → bg is "I started it and changed my mind," & is "send it to the back from the start."Using &
$ sleep 100 & [1] 12345 $
[1] is the job number, 12345 is the PID. Prompt returns immediately.
$ jobs [1]+ Running sleep 100 &
Status is Running, not Stopped.
Real Example: Background Log Collection
$ tail -f /var/log/syslog > mylog.txt & [1] 23456 $ # do other things while it runs $ vim config.txt
Run tail -f in the background while editing a file with vim. Classic parallel workflow.
With & alone, output bleeds into your terminal: A backgrounded command's stdout still prints to your terminal by default. If it's noisy, redirect with > file 2>&1 to send output to a file (or > /dev/null 2>&1 to discard).
5. jobs: See What's Running
jobs. Lists every job your current shell is managing.Basic Usage: jobs
$ sleep 100 & [1] 12345 $ sleep 200 & [2] 12346 $ vim notes.txt # Ctrl+Z to pause $ jobs [1] Running sleep 100 & [2]- Running sleep 200 & [3]+ Stopped vim notes.txt
Three jobs visible.
+= most recent (defaultfg/bgtarget —vimhere)-= previous one (sleep 200)
Target a Specific Job: %n
$ fg %1 # bring sleep 100 to the front $ bg %3 # send vim to background (it stays paused) $ kill %2 # terminate sleep 200
Don't forget the %
kill 1 is the command that sends a kill signal to PID 1 (init). A regular user is blocked by permissions, but root can take down the whole system. Make % muscle memory whenever you reference jobs.
Useful jobs Options
| Option | Effect |
|---|---|
jobs -l |
Also show PIDs |
jobs -p |
PIDs only (handy for scripting) |
jobs -r |
Only running jobs |
jobs -s |
Only stopped jobs |
6. What Happens When You Close the Terminal?
nohup and disown are for. And for the most robust solution, tmux.nohup: Ignore SIGHUP From the Start
$ nohup ./long_script.sh > output.log 2>&1 & [1] 34567
Prefix with nohup and the job ignores SIGHUP when the shell exits. Log out and it keeps running.
disown: Detach From Shell After Launch
$ ./long_script.sh & [1] 45678 $ disown %1 $ exit # closing the terminal won't kill %1
If you started a job with & and then realized you want it to survive logout, disown is your tool.
tmux is more bulletproof
nohup / disown only keep the job alive past disconnect — but you lose the screen output. With tmux the whole session is saved on the server, and you can later tmux attach to come back to the live output. For long jobs where you want to see what happened, tmux wins. → Getting Started with tmux
7. Common Pitfalls
Pitfall 1: Mixing Up Ctrl+C and Ctrl+Z
Symptom: You meant to pause but the job died.
Cause: Confusing Ctrl+C (SIGINT — terminates) with Ctrl+Z (SIGTSTP — pauses).
Fix: Remember "C = Cancel, Z = Zzz (sleep)." Ctrl+Z is non-destructive — your vim work is safe.
Pitfall 2: Typing kill 1 Instead of kill %1
Symptom: kill 1 says "Operation not permitted" (or worse, as root, the system staggers).
Cause: You confused job number with PID and forgot the %. kill 1 sends a termination signal to PID 1 (init / systemd).
Fix: Always prefix job references with %. When in doubt, run jobs -l first to see the PID — or use kill %% to target the most recent job.
Pitfall 3: Background Output Crashing Your Prompt
Symptom: Output from a backgrounded command splatters into the middle of whatever you're typing.
Cause: Stdout and stderr still go to the terminal in the background.
Fix: Redirect with > file 2>&1 & (to a log file) or > /dev/null 2>&1 & (to discard).
Pitfall 4: vim Won't Run After bg
Symptom: You did Ctrl+Z then bg on vim, but jobs shows Stopped (tty input) forever.
Cause: vim needs to read from the terminal. In the background it can't, so the shell auto-pauses it.
Fix: For interactive full-screen apps (vim, top, nano, etc.), stick to Ctrl+Z to pause → fg to resume. Never bg them.
8. Templates: Job Control Without Accidents
Copy-paste: Run a command while editing in vim
# 1. Editing in vim $ vim config.txt # Ctrl+Z to pause # 2. Terminal is back — do other things $ ls /etc/ $ grep "error" /var/log/syslog # 3. Resume vim $ fg
The point is to bounce back and forth without closing vim. Build the rhythm: Ctrl+Z → side task → fg.
Copy-paste: Run a long job in the background
# Send output to a file so it doesn't trash your terminal $ ./long_script.sh > output.log 2>&1 & [1] 12345 # Check progress $ jobs $ tail -f output.log # Terminate if needed $ kill %1
Forgetting > output.log 2>&1 is the most common mistake. Keep them paired.
Copy-paste: Survive logout
# nohup from launch $ nohup ./long_script.sh > output.log 2>&1 & [1] 12345 # Or disown after the fact $ ./long_script.sh > output.log 2>&1 & [1] 12345 $ disown %1 # The most robust approach — tmux preserves the whole screen (recommended) $ tmux $ ./long_script.sh # Ctrl+b → d to detach, tmux attach later to come back
Mini Challenges
Challenge 1: Ctrl+Z ↔ fg Round Trip
$ sleep 30 # About 5 seconds in, press Ctrl+Z $ jobs # → Should show [1]+ Stopped sleep 30 $ ls # try another command $ fg # return to sleep, finish out the remaining time
Challenge 2: Three Parallel Jobs
$ sleep 100 & $ sleep 200 & $ sleep 300 & $ jobs # → all three [1] [2] [3] should appear $ kill %2 # kill just the middle one $ jobs # → [2] should now be gone
Challenge 3: Background With File Output
$ (for i in 1 2 3 4 5; do echo "step $i"; sleep 1; done) > result.log 2>&1 & $ jobs # Running $ cat result.log # output accumulates in the file # When the job finishes, you'll see something like: # [1]+ Done ( for i in 1 2 3 4 5; ...) > result.log 2>&1
Ctrl+Z is huge — I'll never kill vim with Ctrl+C again.Today's Three-Line Summary
Ctrl+Zpauses and frees the terminal,fgbrings it back,bgkeeps it running behind — never accidentally kill vim with Ctrl+C again- Always reference jobs with
%(kill %1≠kill 1) — getting PID and job number mixed up can take down init - For background long-runners, make
& + > file 2>&1a habit, and reach fornohup/disown/tmuxwhen you need to survive logout