Use job control
Reviewer: name contact BSD flavour
Reviewer: Brett brett.maharATgmailDOTcom OpenBSD
Concept
Know how to:
- start a process in the background
- place an existing process into the background, and
- return a background(((background))) process(((processes))) to the foreground(((foreground))).
Be able to verify if any jobs(((jobs))) are currently in the background.
- Be aware of the difference between kill(1) and the shell built-in "kill".
Introduction
Some jobs run by the shell are "small jobs" --- quick and easy for the machine, with virtually no waiting for the user. Consider these small files and the use of grep(1):
$ ls -l
-rw-r--r-- 1 myuser wheel 315 Jan 19 13:00 notes.txt
-rw-r--r-- 1 myuser wheel 1967 Jan 18 13:49 otherstuff
-rw-r--r-- 1 myuser wheel 6335 Jan 11 23:11 packagelist
-rw-r--r-- 1 myuser wheel 14764 Jan 23 13:45 spammers
-rw-r--r-- 1 myuser wheel 2678 Jan 23 13:46 spamstuff.txt
$ grep speakeasy *
spammers:.dsl.speakeasy.net
The job is finished almost instantly, and your shell returns control to you. On the other hand, consider this:
# tar -c -z -f /backup/src.tar /usr/src/*
Depending on your system, it could take a very long time for you to make a gzipped archive of your BSD's project source tree! So, you can wait and twiddle your thumbs, or you can use job control to have the shell "put the job in the background" and return to your prompt so you can keep working.
Job Control
All modern shells feature job control(((job control))). In FreeBSD, the standard user's shell is csh/tcsh, but the information presented here should apply equally well to other shells, except as noted. The manpage for tcsh(1)
(for FreeBSD) or ksh(1)
(for OpenBSD) have an entire section on job control, TODO: which should be read to clarify and extend this section.
TODO shell manpages for NetBSD and DragonflyBSD. TODO: And tcsh is not installed by default on each BSD
With a job-control shell, you can start a job so that it runs in the background, see information about currently running "backgrounded" jobs, move jobs from the foreground to background or vice-versa, or terminate them abruptly.
"Backgrounding" and "Foregrounding" jobs
The shell keeps a list of (backgrounded) jobs, including their status, which can be queried with the "jobs" shell built-in. If/when the job exits, the shell will report this, along with the job's exit status, prior to the next shell prompt.
Listing the process IDs (PID) with the jobs can be done with "jobs -l
". (TODO: check shells)
To start a job in the background, end the command line with "&". To see running jobs, type "jobs" ("jobs -l" is also handy; many systems have "j" aliased to "jobs -l"). You can then use the job numbers in conjunction with "bg %" or "fg %" to move jobs from foreground to background and vice-versa.
If you start a long job in the foreground and then realize you forgot the "&", you can suspend the job with CTL-Z, then issue "bg" to the shell and the job will continue in the background. You might also use "kill -STOP" for suspend, and "kill -CONT" to continue; see below.
See the Examples section for details.
A word about "kill"
A job control shell usually has a built-in 'kill' (((kill))) command; a problem can ensue when this 'kill' is confused with kill(1).
For example, in the tcsh shell you should use a "%" sign to indicate a job number to the shell; otherwise it may be confused with a process ID in the system. While "kill %1" would simply terminate the first backgrounded job, "kill 1" would send a TERM signal to the process with PID 1 (usually /sbin/init!), and would have a similar effect to calling shutdown(8)! As a "normal" user, this would probably not be an issue, but if you were to do this as root, you might cause some problems. (Chalk this up as yet another reason not to do normal work as "root").
Most other shells also have a "kill" built-in. If you use another shell, try "type kill" at the prompt. If the shell doesn't answer "/bin/kill", then it will probably say something similar to "kill is a shell built-in". If your shell has a built-in kill, check your shell's manpage for details on using "kill" under your shell.
Redirecting job output
If you intend to use job control (((job control))) it is useful to know about output redirection, because, under most circumstances, jobs that produce output will continue to do so, potentially "cluttering up" your terminal and whatever your "next project" is. See section Demonstrate proficiency in using redirection, pipes and tees for details.
Examples
$ sh ~/scripts/mylongscript.sh &
sh /home/myuser/scripts/mylongscript.sh &
[1] 10394
Execute "mylongscript.sh" in the background. The shell reports the command, the job number, and the job's PID (((PID))) on the system.
$ tar -c -f /backup/src.tar /usr/src/*
tar: Removing leading '/' from member names
Oops! There's that long job again, and we forgot to background it. While "stuck" waiting, press Ctrl-Z:
^Z
Suspended
Now you have your prompt back, so issue "bg":
$ bg
[2] tar -c -f /backup/src.tar /usr/src/COPYRIGHT /usr/src/LOCKS ... &
Now run "jobs" (((jobs))) and you should see both "backgrounded" (((background))) tasks in the list:
$ jobs
[1] + Running sh /home/myuser/scripts/mylongscript.sh &
[2] - Running tar -c -f /backup/src.tar /usr/src/COPYRIGHT /usr/src/LOCKS ... &
But wait! Suppose a colleague is already making an archive of the source tree, so we don't need to.
$ fg 2
tar -c -f /backup/src.tar /usr/src/COPYRIGHT /usr/src/LOCKS ... &
^C
A quick Ctrl-C, and we save lots of CPU cycles, and disk space, too. Of course, in csh/tcsh, we could have just called "kill" (((kill))) and obtained the same basic result:
$ kill %2
Practice Exercises
Find a "long" job that needs to be run, and do it in the background with "&". (Optionally, use redirection to make sure any output goes to a file or to the "bit-bucket".)
Call the job to the foreground, then terminate it with CTL-C. (Do this fairly early!)
Start the job again in the foreground, then use CTL-Z to suspend the job. Then issue "bg".
While the job (possibly more) is running, call "jobs" and then "jobs -l" (or "jobs -ls" in some shells). Study the differences in the output.
Use kill to terminate the job. Remember that in some shells, you must use "%" ("kill %1"). In others, it may be necessary to obtain the job's PID (which can be done with "jobs -l", or other tools) and use this as a flag to kill(1).
More information
\&, CTRL-Z, jobs, bg, fg, and "kill" which are all built-in to the shell