Wednesday, April 15, 2009

Episode #23: Job Control

Hal Says:

Paul's last post left me hankering to talk a little bit more about job control in the Unix shell. Besides, it'll make Ed feel really bad about the Windows command shell, so what's not to like?

The simplest form of job control is to hit "^Z" to suspend a currently running process. Once the process is suspended, you get your command prompt back and can issue other commands. You can also type "bg" to force the suspended process into the background, where it will continue to run:

# tail -f /var/log/httpd/error_log
[... output not shown ...]
^Z
[1]+ Stopped tail -f /var/log/httpd/error_log
# bg
[1]+ tail -f /var/log/httpd/error_log &

Now you can fiddle with your Apache config file, restart the web server, etc. The "tail -f" process is still running in the background and displaying errors as they appear in the log file. I find this very useful if I'm working remotely to resolve problems on my web server. Actually, I could have simply started that "tail" command in the background by putting a "&" at the end of the command line:

# tail -f /var/log/httpd/error_log &
[1] 14050

I often use job control to flip in and out of a superuser shell:

$ /bin/su
Password:
# [... do some stuff as root ...]
# suspend

[1]+ Stopped /bin/su
$ [... do some stuff as a normal user ...]
$ fg
/bin/su
#

Notice that you use "fg" to start a suspended process running again. This sure beats having to keep using "su" and re-enter the root password all the time. Of course, you probably should be using "sudo", but that's a topic for another day.

By the way, you can also use job control to blip in and out of remote SSH sessions too:

[hal@localhost ~]$ ssh remotehost
hal@remotehost's password:
Last login: Sun Apr 5 10:06:26 2009 from ...
[hal@remotehost ~]$
[hal@remotehost ~]$ ~^Z [suspend ssh]

[1]+ Stopped ssh remotehost
[hal@localhost ~]$ fg
ssh remotehost

[hal@remotehost ~]$

Note that the key sequence to suspend an SSH session is "\n~^Z"-- you have to enter the tilde character at the beginning of a line, not while you're in the middle of a command-line. By the way, if you're logged into multiple machines in series, you can use multiple tildes to pick which of the chained SSH sessions you actually want to suspend.

Sometimes you might have multiple jobs suspended at the same time. You can use the "jobs" command to display them, and use the job number displayed in the output to interact with the different jobs:

# jobs
[1] Stopped vi /etc/httpd/conf/httpd.conf
[2]- Stopped vi /etc/httpd/conf.d/ssl.conf
[3]+ Stopped tail -f /var/log/httpd/error_log
# bg %3
[3]+ tail -f /var/log/httpd/error_log &
# fg %1
vi /etc/httpd/conf/httpd.conf
^Z
[1]+ Stopped vi /etc/httpd/conf/httpd.conf
# %2
vi /etc/httpd/conf.d/ssl.conf
^Z
[2]+ Stopped vi /etc/httpd/conf.d/ssl.conf
# fg
vi /etc/httpd/conf.d/ssl.conf

In general you do "[fg|bg] %n" to foreground or background job number "n". However, you can leave off the "fg" if you want because foregrounding a job is the default. If you don't specify a job number after the "fg" or "bg", then the most recently manipulated job number is assumed-- you can see this job with a "+" next to it in the output of the "jobs" command.

Ed Wimpers:

Job control?!? What are you, Hal... just plain cruel? I long for true job control in my cmd.exe. If I want true job control, I just install Cygwin.

But, there are some job control-like things I can do in a pinch on a box without Cygwin.

For example, if I want to start a process in a separate cmd.exe window, I can use the start command. Here, I'll use it to start a separate shell window to search for wmic.exe on the C:\ partition, using the file search capabilities we described in Episode #21:

C:\> start dir /s /b c:\wmic.exe
That pops up a separate Window that will display my results. The window will linger displaying my results after it's done. If I want to start it up minimized, I can run:

C:\> start /MIN dir /s /b c:\wmic.exe

If I want it to start a process in a separate window and then have it disappear when it is done running, I use:

C:\> start cmd.exe /c dir /s /b c:\wmic.exe

Sadly, there's no easy way to get access to standard out of these separately invoked shells windows while they are running, other than to have them dump their output into a file, with "> filename" at the command line invocation.

But now for the tricky part... how can you mimic the & feature of most Linux shells to run something in the background of the current cmd.exe while still dumping their standard output into the current shell's display? We can use the /b option of start to put something in the background, as in:

C:\> start /b dir /s /b c:\wmic.exe

So, we've got &-like behavior! However, it's not really full job control, because I can't pull that job into the foreground, or select one job from a group of backgrounded tasks. But, I can kill my background task, by hitting CTRL-break in the cmd.exe window that spawned it. Also, each time you run "start /b", it will leave an extra cmd.exe process running until you either exit that process (by typing... you guessed it... "exit") or killing it.

To kill "jobs" (if I can call them that) more thoroughly, I usually rely on wmic or taskkill, as defined in Episode #22.

So, there you have it... not quite job control, but an ability to kick things into the background so you can keep your current shell active and get more work done. Oh, and install Cygwin. You'll be glad you did. :)

Paul Says:

I will never forget when I was first learning Linux/UNIX (I actually got my feet wet with Linux/UNIX by using Linux at home, and AIX at work). I was configuring a proxy server, carefully constructing each command line switch and running the command. I'd hit ^Z to put the command in the background and wonder why my proxy server wasn't listening. I had forgotten to put the & after the command, so it truly did "suspend" the command. I never made that mistake again :)

I would also like to quickly highlight the nohup command. I often use this when I run a command that will take a long time and want to go back and check its output:

$ nohup nmap -sS -T4 -iL edswindowsnetwork -oA myscanresults &


Sometimes processes you start while logged into a remote host will SIGHUP when you logoff, and nohup can prevent that.