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.