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 number n 
  • %abc: Refers to the job started by a command beginning with abc. For example, for our sleep 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 containing abc. 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 (smile)

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 (smile))

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

  • No labels