Development

Getting to Know Your Shell’s Job Control Features

By November 25, 2014 No Comments

This post is about job control in Unix shells.

Job control is your command line shell’s built-in interface for managing multiple jobs. Although I’ve known about and used job control features like & for a long time, I only recently discovered how they all fit together. With your terminal emulator’s tabs and programs like tmux and screen, it’s possible to sidestep your shell’s job control system, but if you did, you’d be missing out on some great tools.

In this article, I’ll cover using job control to handle a few common use cases. For the examples below, I’ll assume you’re using Bash, although other popular shells like Zsh, Ksh and Fish should generally work the same way (see this comparison for details).

Start a Command in the Background

Many developers will be familiar with this one already:

$ ./my-long-running-task &

The ampersand (&) tells Bash to run the command in the background, allowing you to run other commands while it’s executing. After starting it in the background, Bash displays information about the job:

[1] 37868

This means the command is running as job number 1, with the PID 37868. We’ll use the job number in the examples below.

We can pull up this information at any time with the jobs command, which lists all of the shell session’s active jobs:

$ jobs -l 
[1]- 37868 Running                 ./my-long-running-task &

Pause the Active Job and Resume It in the Background

I’ll often run a command and suddenly wish I had started it in the background (as we did in the previous example). Instead of opening a new terminal tab or waiting for the command to finish, we can use job control commands to move the running job to the background.

First, we’ll pause the running command by typing the suspend character: Control-Z on your keyboard.

$ ./my-long-running-task
^Z
[1]+  Stopped                 ./my-long-running-task

This causes Bash to send the process a SIGSTP, which suspends the process (stopping it in a resumable fashion, rather than terminating or quitting it). Once you’ve hit Control-Z, you should see a line containing the job id and the current state of the job: “Stopped”.

Next, we’ll resume the job in the background. For this, we’ll use the bg (background) command, which does just that. As argument, we include the job number led by a percentage sign (%). See below for more details on this format.

$ bg %1
[1]+ ./my-long-running-task &

The bg command sends the process a SIGCONT, which continues execution from where it previously left off (when it was stopped with SIGSTP). The task is now running in the background.

About the Job Spec

The bg command (like the jobs, fg and kill commands) takes a “job spec” as argument. This is a special format that contains the job number, a substring of the command, or one of the other shortcuts below:

  • %n, where n is a job number (e.g., fg %1)
  • %str, where str is the beginning of the command that started the job. (e.g., bg %mysqldump)
  • %?str, where str is a substring of the command that started the job. (e.g., kill %rails will match bundle exec rails &)
  • %% and %+ match the current job (that is, the job most recently started in the background or suspended, as you can’t run these commands if a job is executing in the foreground). If you don’t include a job spec when calling fg or bg, this is also the default. You’ll notice we could have simply called bg without the %1 above.

Pause the Active Job, Do Something Else, Then Resume the Job

This one is particularly useful if you run editors like Vim or Emacs from the terminal. Job control allows you to pause the editor, run some other commands in your shell, then resume editing.

As with the previous job, we’ll use Control-Z to suspend the running command:

$ ./my-long-running-task
^Z
[1]+  Stopped                 ./my-long-running-task

This time, instead of using the bg command, we’ll resume the command with the fg (foreground) command:

$ fg
[1]+ ./my-long-running-task &

This again sends the SIGCONT signal, but this time returns the job to the foreground, making it the current job.

Kill a Job

The kill command, in addition to accepting a PID, also takes a job spec. For example, to kill a job with the number 4:

$ kill %4

This is a good time to mention that (depending on your shell and shell configuration) background processes running in your shell may be killed when your shell session ends. In Bash, this behavior is controlled by the huponexit option. If you need to run commands that will outlive your shell session, there are at least a few other options:

  • Run the command from a terminal multiplexer like tmux or screen that allows for a persistent session.
  • Run the command with nohup, which causes it to ignore the HUP signal sent on shell logout.
  • Use the disown command. True to its name, when you disown a job, the shell session forgets about it completely (doesn’t even call it on holidays). This way, the HUP signal is also not sent when the shell session ends.

That’s it for now. If you have any tips or corrections, please give us a shout in the comments below. For more information about job control in your favorite shell, check out the documentation below:

Web Application Startup Guide

A 30-page ebook that covers positioning, marketing, pricing, and building your startup product, plus more.