T••LBX: Blog

Introduction To Jobs In The Terminal

Essentially, jobs are processes which you started from your terminal session. You see all processes with the ps command, but processes which you started from one terminal session are also in your jobs and the shell has built-in functions to operate on them and improve your workflow.

Note that the examples below are using built-in functions, therefore I know they work in zshell and bash, but there might be differences in other shells.

The Basics

Most of the commands you type are executed quickly and you cannot see them in your jobs. So in order to see something, let's run something intentionally long.

$ sleep 500

Type this in your terminal and run it. Now you normally would be waiting 500 seconds for your prompt to be back. But here is something important to know: The shell is always a Ctrl+Z away.

Go ahead and type Ctrl+Z. You probably know about Ctrl+C if you deal with servers sometimes, or other long running processes. While Ctrl+C stops your job completely, Ctrl+Z only pauses it. You get back your prompt, can type other commands, and then resume your paused job whenever you want.

Go ahead and use the jobs command. You should see something like this:

$ jobs
[1]  + suspended  sleep 500

So the purpose of the jobs command is to show you the jobs with their state (suspended in this case) and an index. This index or job number is important because it can be passed to other commands as a reference.

Let say we wanted to kill this job, we can use the kill command. But instead of looking for its process ID, we only need to use the job number prefixed with %. This is important to always prefix them with % so that the command does not see it as a process ID.

So here we go:

$ kill %1

Now normally if you run jobs again, it should be empty.

We obviously could get the process ID of the last run command with echo $! but with jobs index, it does not have to be the last run command.

A Practicle Example

Okay now let's imagine a more practical case. You are working on a web project and you need to do some modifications. You start vim (or any other terminal-based editor). Then you start to edit your files and comes the time to try it in the browser. Which means that you need to start your web server.

Once again, the terminal is always a Ctrl+Z away. So let's pause vim with Ctrl+Z, and let's start our server:

$ bundle exec sinatra &>/dev/null &

Here is a weird command if you are not used to it. The basic command itself does not matter, here we are starting a ruby/sinatra application but it could be any other server for any language. The end of the command is more relevant to our topic. Basically the & at the very end makes this a background job, meaning that it does the same thing as without, but you get your prompt back. It runs in the background.

This is the other concept important to get about jobs. There are foreground jobs and background jobs. While a command is executing in your prompt, it is in the foreground. Any quick command is in the foreground for the duration of its execution. It is the same for long commands and this is why you don't get your prompt back until it is finished, or until you do something to stop it or pause it.

When you put a job in the background, you get back your prompt but it is not stopped or paused. It is still running in the background doing its job. You can start any command in the background by adding & at the end. Now the problem is that a command in the background is still printing messages in your prompt since this terminal is still its stdout and stderr. This could be quite disruptive.

The &>/dev/null has an explanation which exceeds the scope of this tutorial, but just know that it just prevents the log messages from being printed on your screen. It discards them. A better option would obviously be to redirect this to a log file instead of /dev/null.

Now if we run the jobs command again you'll see something like:

$ jobs
[1]  + suspended  vim .
[2]  + running    bundle exec sinatra &>/dev/null &

You now see 2 jobs. Please note the difference, the server is not paused/suspended. It is currently running and you can check your browser. Whereas when a command is paused, it stops its execution for a while. For example when we paused the sleep command, then it stopped counting. You have to resume a paused command if you want it to carry on with its work. Which leads me to our next step: Going back to vim.

One way to resume a job is to make it a foreground job. You can put a job to background with the bg command, or to the foreground with the fg command. If your text editor is job number 1 like in our example, then you can resume with:

$ fg %1

You're back in the text editor. Also fg defaults to the last job you left, which means that you can go back and forth of any job with Ctrl+Z and fg. You don't have to type the job number in this case.

Now let's say you've started your server but forgot to make it a background job. What you can do is pause it with Ctrl+Z and run bg immediatly. And then your server will be running again, but in the background. You don't need to type a job number because it is the last job you've paused. And note that fg and bg are also resuming the execution of a paused job.

Conclusion

This probably will not change your world, but it is still satisfying to have more control on different jobs in a terminal session instead of having to use ps and process IDs which are quite long numbers compared to job numbers.

Here are 2 last tips:

The first is that if you want to refer to a job and you know for sure that it is the only job containing a certain word, you can refer to this word instead.

For example we could have stopped our server with:

$ kill %?sinatra

Note the % which means we are refering to a job, and the ? which tells the shell to look for that word
in the command instead of a job number.

The last tip is that if you use jobs a lot and also have multiple terminal tabs, it becomes very useful to always have the number of jobs written somewhere in your prompt. That way you can end them properly before closing your tab.

If you use zshell, the placeholder for having the number of jobs in your prompt is %j. I personnaly have this %(1j. %{$fg[red]%}%j.) which puts the number of jobs in red, but only if there is at least 1 job.


Tags:     unix shell linux commandline process bash zshell zsh job