We've seen in Chapter 6 how we can manage processes on a global scale. However, we can also have a notion of processes within our shell instance, known as jobs. Jobs are simply sub-processes of our shell, and we'll explore how we can work with them in this chapter!
Viewing Current Jobs
To view the current jobs that are running in our shell, we can use the jobs
command:
jobs
However, when you run this for the first time, you won't get any output - this is because there aren't any other jobs running in our shell! Lets get some other jobs running so that we can see what they look like.
To do this, we'll suspend a job. This is exactly what it sounds like; we'll temporary stop a job from executing. To suspend a job, simply press Ctrl+Z
from within the shell. We can observe this using the sleep
command, which takes in a number of seconds, and simply delays (or "sleeps") for that time:
sleep 500 # Sleep for 500 seconds # Press Ctrl+Z
After pressing Ctrl+Z
, let's observe the output of jobs
:
jobs
From this, you should get some output that looks like the following:
[1] + suspended sleep 500
This tells us a couple things. The "1" is the job number - it acts as a reference to the job. The "+" indicates that the sleep
job is our "current" job, and acts as the default for other commands that we'll explore in a moment. It also tells us the jobs that's running (sleep 500
), and its current status (suspended). Let's suspend a few more jobs to get a more verbose list:
sleep 200 # Press Ctrl+Z sleep 100 # Press Ctrl+Z
After this, your jobs
output should look like this:
[1] suspended sleep 500 [2] - suspended sleep 200 [3] + suspended sleep 100
Notice that we have a unique job number for all of our jobs. Notice also that the "current" job (the one indicated by "+") has been updated to our most recent. Finally, a new symbol snuck in; the "-". This indicates the notion of a "previous" job - the one before our current one
As a reminder, all of these jobs are simply processes that have the shell as a parent. If you wish to see their PID's, you can use jobs
with the l
flag to get more information (similar to ls
):
jobs -l
Job Specification
Alright - we've got a bunch of suspended jobs, we probably want a way to clean them up. One natural approach might be to use the kill
command, which we've used before to clean up processes. Normally, kill
takes in the PID of the process; while we could do the same thing here, there's an easier way to specify jobs using a job specification. Job specifications (or "jobspecs") begin with percent sign (%
), and can take a variety of forms:
%n
: Refers to the job with job numbern
%abc
: Refers to the job started by a command beginning withabc
. For example, for oursleep
command, this might be%sle
. If multiple jobs match, defaults to the most recent (highest job number)%?abc
: Refers to the job started by a command containingabc
. If multiple jobs match, defaults to the most recent (highest job number)%+
: Refers to our "current" job (the one indicated with "+")%-
: Refers to our "previous" job (the one indicated with "-")
kill
can also take in job specifications in place of the PID to kill jobs. For example, if we wanted to kill the sleep 500
job from above, we might use:
kill %1 # Kill the job with job number 1
If you do so, you should get confirmation from the kill
command; it looks just like the entry for the job in the job table, but with the status now as "terminated". The job is now killed and removed from the job table
On your own, see if you can kill the remaining sleep
jobs, using a variety of job specifications
Background Jobs
Given that our shell can support multiple jobs, it makes sense that we might want to be able to run some in the background. Running jobs in the foreground means that the terminal will wait until they're done before giving you the prompt back, whereas jobs in the background have minimal interaction. We'll first explore how we can take a process from suspended to running in the background. Let's suspend another sleep
job, but only for 10 seconds this time:
sleep 10 # Press Ctrl+Z
Verify with jobs
that our sleep
job is suspended. Next, we'll use the bg
command to take this jobs from suspended to running in the background.
bg
bg
takes a job and starts it running in the background. By default, it acts on the "current" job (the one designated by "+"), but you can give it any job specification as well (as well as PID's, although that is less common). When you do so, your terminal should print out the job's entry with the status as "continued". Once you wait 10 seconds for the job to finish, you should also get a similar notification with the status as "done", to notify you that the background job has completed.
Let's do this one more time, with a longer command:
sleep 500 # Press Ctrl+Z bg
Now, let's look at our jobs
output:
jobs
Notice that the status is now "running", as the job is running in the background
If you ever wish to know when a job finishes, you can use the wait
command. It acts similarly to bg
in that it acts by default on the current job, but can take in any with a jobspec or PID, and waits to complete until the given job completes. However, since it's not really a job itself, you can't suspend or kill it, so waiting on a long job may result in your terminal being tied up for a while
Lastly, what if we know that we want to start a job in the background immediately? In other words, we want to start execution in the background to free up our terminal. To do this, you can simply specify an ampersand (&
) after the command, which will print out the PID for the job and start it in the background:
sleep 500 & # Run `sleep 500` in the background
(Note that the PID is useful, especially if you realize right after that you don't want to do that and want to kill it )
One other fun fact; while the $
environment variable holds the PID of the current process, !
holds the PID of the previous process. That means that if you start a program running in the background, the command kill $!
will always kill it
Foreground Jobs
The last thing that we can do is move either a suspended or background job into the foreground (i.e. the main job associated with the terminal). Let's start with a sleep
job running in the background:
sleep 500 &
From here, we can use the fg
command. This acts similarly to bg
(on the default job, or any jobs specified with a jobspec or PID), and runs it as the main job connected to the terminal in the foreground. For example, we can move our sleep
job to the foreground with
fg
Similar to before, the terminal will indicate that the job is running. However, you won't get the prompt back, as the terminal is now associated with the running process. It is as if you ran just sleep 500
from the beginning. With this, you should now be able to examine jobs that are suspended, in the background, or in the foreground, as well as move jobs between these statuses