sebastiano.tronto.net

Source files and build scripts for my personal website
git clone https://git.tronto.net/sebastiano.tronto.net
Download | Log | Files | Refs | README

job-control.md (6310B)


      1 # Job control: one shell is all you need
      2 
      3 If you work in a graphical desktop environment, you are most likely
      4 used to multi-tasking. You can listen to music while you type your
      5 blog posts, browse the web while your code compiles, and so on.
      6 
      7 But what if you are interacting with your machine via a command
      8 line?  Sure, you can use a
      9 [terminal multiplexer](https://en.wikipedia.org/wiki/Terminal_multiplexer)
     10 like [tmux](https://tmux.github.io).
     11 But what if you forgot to run it before starting your tasks? And now
     12 you are updating your packages and the shell is locked?! What a nightmare!
     13 
     14 Luckily, the UNIX shell has a job control system that allows you
     15 to run multiple tasks at the same time. What I explain in this post
     16 has been tested on ksh on OpenBSD, but should work in the same way
     17 on Linux or any UNIX-like OS with a POSIX-compatible shell - except
     18 perhaps for the key combinations to suspend or kill a job.
     19 
     20 This post is not meant to be an introduction to what the shell is,
     21 but it is worth spending a few words to clear up some common (and
     22 understandable) confusion. I'll keep them really *a few*.
     23 
     24 ## The terminal vs the shell, in 42 words
     25 
     26 A [terminal emulator](https://en.wikipedia.org/wiki/Terminal_emulator)
     27 is a program that displays text, usually coming from a
     28 [shell](https://en.wikipedia.org/wiki/Shell_(computing)).
     29 The shell reads text, normally entered by the user, and
     30 interprets it. If this text instructs it to run another program, it
     31 spawns a new [process](https://en.wikipedia.org/wiki/Process_(computing))
     32 for it.
     33 
     34 ## Foreground and background jobs
     35 
     36 A *job* is an entity that runs in the shell when you issue a command.
     37 A job can consist of more than one process, for example if you
     38 run multiple commands in a
     39 [pipeline](https://en.wikipedia.org/wiki/Pipeline_(Unix)).
     40 When you try to close a shell, it usually warns you if you have
     41 running jobs *attached* to it, since it will *kill* them before
     42 it closes - that might depend on the shell's configuration and
     43 launch options, though.
     44 
     45 When you run a program by typing its name of in a shell, say a text
     46 editor like vi, this program "takes over" the shell. You can't run
     47 any other command until this program terminates - or so it seems.
     48 This happens also with most GUI programs, like firefox or gedit:
     49 if you run them from a shell you will see some log messages, but
     50 the shell is otherwise useless. This is the intendend behavior, and
     51 this job is said to be running in the *foreground*. At any time
     52 there can be at most one foreground job in a given shell.
     53 
     54 If you have read my previous
     55 [blog entry on the shell](../2022-09-13-sh-1), you should know that
     56 you can launch a command in the *background* by adding an `&`
     57 at the end of the line. Background jobs do not "block" the shell,
     58 but they still use it to print their output.
     59 
     60 So jobs can be running in the foreground or in the background of a
     61 shell. There is also a third possible state for a shell's job: it
     62 can be *suspended*. On most UNIX shells you can suspend a foreground
     63 job by pressing `Ctrl`+`Z`. Try it: if you open `vi`, or any other
     64 terminal-based program, and press `Ctrl`+`Z`, you are sent back to
     65 a command prompt. This is even more fun with a graphical application:
     66 its whole window becomes unresponsive and you can't even close it!
     67 (Ok, I admit my definition of "fun" might be... unusual)
     68 
     69 ## Full job control
     70 
     71 So far we have seen how to run commands (jobs) in the foreground
     72 (default behavior) or in the background (using `&`), and how to
     73 suspend the job in the foreground (with Ctrl+Z). But we can do more.
     74 
     75 To get an overview of the jobs attached to the current shell, you
     76 can use the `jobs` command. If you run it while you have some
     77 backgrounded or suspended jobs, you'll something like this:
     78 
     79 ```
     80 [3] + Suspended   vi
     81 [2] - Suspended   cat
     82 [1]   Running     ./git/nissy/nissy
     83 ```
     84 
     85 The number in brackets is the job's *ID*. It is followed by the
     86 job's status (Done, Running, Suspended or Stopped) and the command
     87 that started the job. If you use the `-l` option you'll get the
     88 job's *process ID*, or PID, too.
     89 
     90 You can use this information to change a job's status with the
     91 `fg` and `bg` builtins. For example, typing
     92 
     93 ```
     94 $ fg %job_id
     95 ```
     96 
     97 makes a currently backgrounded or suspended job run in the foreground.
     98 Similarly
     99 
    100 ```
    101 $ bg %job_id
    102 ```
    103 
    104 makes a currently suspended job run in the background. As far as I
    105 know, there is no way to tell the job running in the foregroung to
    106 pass to the background - you have to suspend it first, and then use
    107 `bg`.
    108 
    109 You can replace `%job_id` with the job's PID (without percent
    110 symbol) or with `%string`, where `string` is the beginning of the
    111 job's name.  If you call `fg` or `bg` without any argument, the job
    112 marked by a `+` in the jobs list is selected. You can select the
    113 job marked by a `-` with `%-`
    114 
    115 All of this is summed up in the following diagram:
    116 
    117 ![Diagram showing how to change the status of a job](jobs-diagram.png)
    118 
    119 *Picture generated with [tikz](https://github.com/pgf-tikz/pgf)
    120 ([code 1.2Kb](jobs-diagram.tex), [pdf 27.3Kb](jobs-diagram.pdf)).
    121 Do you know a better (simpler) tool or language to generate svg
    122 graphics and diagrams programmatically? Let me know!*
    123 
    124 You may have noticed that [kill(1)](http://man.openbsd.org/kill)
    125 makes an appearance. I have not talked about it yet, and I won't
    126 go over it in detail in this post, but to put it briefly you can
    127 use `kill` to send certain [signals](http://man.openbsd.org/signal)
    128 to a process or job - such as `SIGSTOP` to suspend and `SIGTERM` to
    129 terminate. This is what pressing a Ctrl+Z or Ctrl+C actually does
    130 under the hood.
    131 
    132 ## Conclusion
    133 
    134 With just a few simple bultins and keyboard sortcuts, the UNIX shell
    135 gives some good flexibility in managing running jobs. If, like me,
    136 you run most of your shells in a graphical terminal emulator or in
    137 a tmux session, you can already get all the flexibility you want
    138 by opening a new terminal window. But the ability to suspend jobs
    139 and resume them later might be something new. Moreover, you might
    140 find yourself in a situation where spawning a new window is not an
    141 option - for example if you are connected to a remote machine via
    142 ssh and you forgot to run tmux when you logged in.
    143 
    144 I hope you found this post interesting. I certainly enjoyed
    145 writing it, and I learnt a couple of new tricks in the process.