Skip to content
Lesson 16 of 16

Step 1 of 5 · Reading · ~3 min

Read

Production Concerns

Job Control: fg, bg and jobs

After this lesson your shell keeps a table of jobs, so a program can be started in the background, stopped with Ctrl-Z, resumed in either place, listed with jobs, and reported when it finishes. You will reproduce the exact layout of bash's job lines, including the + and - markers that say which job a bare fg or bg would pick.

A job is a command the shell is tracking

When you run sleep 100 &, the shell starts the program without waiting for it and records a job: a small integer id, the process id, the command text and a state. The ids are what the user types after a percent sign (fg %2). The shell prints the id and the pid when it starts a background job:

[1] 5001

A job is in one of three situations. In the foreground it owns the terminal and the shell is waiting for it. Running in the background, it runs while the shell accepts new commands. Stopped, it has been suspended (Ctrl-Z sends SIGTSTP to the foreground job) and waits to be resumed with SIGCONT.

Commands

CommandEffect
cmd &start in the background, print [id] pid
cmdstart in the foreground and wait
Ctrl-Zstop the foreground job, print its line
bg [spec]resume a stopped job in the background, print [id]+ cmd &
fg [spec]resume a job in the foreground, print its command
jobslist all jobs

A job spec is %n for job n, %% or %+ for the current job and %- for the previous one. With no spec, fg and bg use the current job.

Current and previous

The job lines carry a marker: + for the current job, the one a bare fg picks; - for the previous one; a space for all others. The current job is the one touched most recently, and "touched" means started, stopped or resumed. So after you stop a job it becomes current, even if a newer background job exists. Keep a list ordered by recent use and read the markers off the last two entries.

The line format

Each line has the shape [id], the marker, two spaces, the state padded with spaces to 23 columns, one more space, and the command. A running background job shows a trailing &; a stopped job does not:

[1]-  Stopped                 vim notes.txt
[2]+  Running                 make all &

When a background job ends, the shell reports it in the same layout with state Done, or Exit N when the status is not zero. A foreground job that finishes is not reported. A job id is one more than the highest id still in use, or 1 when there are none, so ids are reused after jobs end; pids keep increasing.

Errors

A spec that names no job gives bash: fg: %3: no such job, and a bare fg or bg with nothing to pick gives bash: fg: current: no such job. Resuming a job that is already running in the background gives bash: bg: job N already in background.

Common mistakes

  • Giving the + marker to the newest job instead of the most recently touched one.
  • Padding the state to the wrong width, or adding the trailing & to stopped jobs.
  • Reusing pids when ids are reused.
  • Reporting a foreground job's completion.

Your exercise

The starter holds the job table and the helpers that format a line and update recency. You write the command handler. The most likely mistake is not marking a stopped job as the most recently used.

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…