Shell scripts were designed to run commands. Up to this point, all the scripts in the book have launched various commands, but all in isolation. The most you've seen so far is piping commands to connect the output of one command to the input of another. But the commands run from the scripts do not provide data back to the scripts, other than through writing data to a file. To make processes better fit into shell scripts, you need the capability to start and stop processes, as well as capture the output of processes into shell variables.
This chapter delves into processes and shows how you can launch and control processes from your scripts, including:
Exploring the processes running on your system
Launching processes in the foreground and the background
Using command substitution to set variables from commands
Checking the return codes of processes
There is a lot of terminology associated with processes. Don't be daunted; it's easier to understand when you see it in action than to explain.
Simply put, a process is a running program. A program is a file on disk (or other storage) that can be executed. Most programs are compiled into the binary format required by the processor chip and operating system. For example, the ls
command is a program, compiled for a particular system. An ls
program compiled for Linux on a Pentium system will not run in Windows, even on the same computer and processor chip. That's because the operating system defines the format of executable binary files.
A command is a program that is part of the operating system. For example, ls
is a command in Linux and Windows systems, while format.exe
is a command in Windows.
The act of making a program stored on disk into a process is called launching or running. The operating system reads the program file on disk, creates a new process, and loads the program into that process. Some operating systems allow for multiple processes to run from the same program. Other operating systems impose a limit, such as allowing only one instance of a program to run at any given time.
There are a lot of differences between operating systems and how they handle processes. Luckily, shells abstract a lot of the details, making for a more consistent view.
When the operating system launches a process, it gives the process and ID. You can view this ID if you list the running processes with the ps
command on Unix and Linux systems, or use the Task Manager in Windows. Figure 9-1 shows the Windows XP Task Manager.
In Figure 9-1, each process has a process ID, or PID. Each process ID uniquely identifies one process. The process ID is the number you need if you want to control or terminate the process.
Isn't it odd that the main way you interact with a running process is to terminate it?
The ps
command lists the active processes. For example:
$ ps -ef
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 Oct08 ? 00:00:05 init [5]
root 2 1 0 Oct08 ? 00:00:00 [ksoftirqd/0]
root 3 1 0 Oct08 ? 00:00:02 [events/0] root 4 3 0 Oct08 ? 00:00:00 [kblockd/0] root 6 3 0 Oct08 ? 00:00:00 [khelper] root 5 1 0 Oct08 ? 00:00:00 [khubd] root 7 3 0 Oct08 ? 00:00:10 [pdflush] root 10 3 0 Oct08 ? 00:00:00 [aio/0] root 9 1 0 Oct08 ? 00:00:10 [kswapd0] root 117 1 0 Oct08 ? 00:00:00 [kseriod] root 153 1 0 Oct08 ? 00:00:04 [kjournald] root 1141 1 0 Oct08 ? 00:00:00 [kjournald] root 1142 1 0 Oct08 ? 00:00:08 [kjournald] root 1473 1 0 Oct08 ? 00:00:00 syslogd -m 0 root 1477 1 0 Oct08 ? 00:00:00 klogd -x rpc 1505 1 0 Oct08 ? 00:00:00 portmap rpcuser 1525 1 0 Oct08 ? 00:00:00 rpc.statd root 1552 1 0 Oct08 ? 00:00:00 rpc.idmapd root 1647 1 0 Oct08 ? 00:00:00 /usr/sbin/smartd root 1657 1 0 Oct08 ? 00:00:00 /usr/sbin/acpid root 1858 1 0 Oct08 ? 00:00:00 /usr/sbin/sshd root 1873 1 0 Oct08 ? 00:00:00 xinetd -stayalive -pidfile /var root 1892 1 0 Oct08 ? 00:00:01 sendmail: accepting connections smmsp 1901 1 0 Oct08 ? 00:00:00 sendmail: Queue runner@01:00:00 root 1912 1 0 Oct08 ? 00:00:00 gpm -m /dev/input/mice -t imps2 root 1923 1 0 Oct08 ? 00:00:00 crond xfs 1945 1 0 Oct08 ? 00:00:01 xfs -droppriv -daemon daemon 1964 1 0 Oct08 ? 00:00:00 /usr/sbin/atd dbus 1983 1 0 Oct08 ? 00:00:00 dbus-daemon-1 --system root 1999 1 0 Oct08 ? 00:00:00 mdadm --monitor --scan root 2017 1 0 Oct08 tty1 00:00:00 /sbin/mingetty tty1 root 2018 1 0 Oct08 tty2 00:00:00 /sbin/mingetty tty2 root 2024 1 0 Oct08 tty3 00:00:00 /sbin/mingetty tty3 root 2030 1 0 Oct08 tty4 00:00:00 /sbin/mingetty tty4 root 2036 1 0 Oct08 tty5 00:00:00 /sbin/mingetty tty5 root 2042 1 0 Oct08 tty6 00:00:00 /sbin/mingetty tty6 root 2043 1 0 Oct08 ? 00:00:00 /usr/bin/gdm-binary -nodaemon root 2220 2043 0 Oct08 ? 00:00:25 /usr/bin/gdm-binary -nodaemon root 2231 2220 0 Oct08 ? 05:52:20 /usr/X11R6/bin/X :0 -audit 0 -au root 2805 1 0 Oct08 ? 00:00:00 /sbin/dhclient −1 -q -lf /var/li root 18567 3 0 Oct18 ? 00:00:09 [pdflush] root 20689 1 0 Nov03 ? 00:00:00 /usr/libexec/bonobo-activation-s ericfj 22282 1 0 Nov04 ? 00:00:00 /usr/libexec/bonobo-activation-s ericfj 25801 2220 0 Nov06 ? 00:00:02 /usr/bin/gnome-session ericfj 25849 25801 0 Nov06 ? 00:00:00 /usr/bin/ssh-agent /etc/X11/xinit ericfj 25853 1 0 Nov06 ? 00:00:01 /usr/libexec/gconfd-2 5 ericfj 25856 1 0 Nov06 ? 00:00:00 /usr/bin/gnome-keyring-daemon ericfj 25858 1 0 Nov06 ? 00:02:12 metacity --sm-save-file 10940799 ericfj 25860 1 0 Nov06 ? 00:00:03 /usr/libexec/gnome-settings-daem ericfj 25865 1873 0 Nov06 ? 00:00:08 fam ericfj 25879 1 0 Nov06 ? 00:00:04 xscreensaver -nosplash ericfj 25888 1 0 Nov06 ? 00:00:13 gnome-panel --sm-config-prefix /ericfj 25890 1 0 Nov06 ? 00:00:28 magicdev --sm-config-prefix /mag ericfj 25895 1 0 Nov06 ? 00:00:39 nautilus --sm-config-prefix /nau ericfj 25909 1 0 Nov06 ? 00:00:03 eggcups --sm-config-prefix /eggc ericfj 25912 1 0 Nov06 ? 00:00:00 /usr/libexec/gnome-vfs-daemon - ericfj 25914 1 0 Nov06 ? 00:00:10 gnome-terminal --sm-config-prefi
ericfj 25939 1 0 Nov06 ? 00:00:01 /usr/bin/pam-panel-icon --sm-cli root 25944 25939 0 Nov06 ? 00:00:00 /sbin/pam_timestamp_check -d roo ericfj 25946 1 0 Nov06 ? 00:00:00 /usr/libexec/mapping-daemon ericfj 25948 1 0 Nov06 ? 00:00:04 /usr/libexec/nautilus-throbber ericfj 25949 25914 0 Nov06 ? 00:00:00 gnome-pty-helper ericfj 25950 25914 0 Nov06 pts/92 00:00:00 bash ericfj 25959 25914 0 Nov06 pts/93 00:00:00 bash ericfj 25962 25914 0 Nov06 pts/94 00:00:00 bash ericfj 26007 1 0 Nov06 ? 00:00:02 /usr/libexec/clock-applet --oaf- ericfj 26009 1 0 Nov06 ? 00:00:01 /usr/libexec/notification-area-a ericfj 26011 1 0 Nov06 ? 00:00:01 /usr/libexec/mixer_applet2 --oaf ericfj 26018 1 0 Nov06 ? 00:00:28 /usr/libexec/wnck-applet --oaf-a ericfj 26020 1 0 Nov06 ? 00:00:04 /usr/libexec/wireless-applet --o ericfj 26022 1 0 Nov06 ? 00:00:02 /usr/libexec/gweather-applet-2 ericfj 26025 25950 0 Nov06 pts/92 00:00:00 /bin/sh /home2/ericfj/bin/favs ericfj 26026 26025 0 Nov06 pts/92 00:00:32 xmms /home2/ericfj/multi/mp3/fav root 26068 1 0 Nov06 ? 00:00:00 [usb-storage] root 26069 1 0 Nov06 ? 00:00:00 [scsi_eh_12] ericfj 26178 1 0 Nov06 ? 00:00:00 /bin/sh /usr/lib/firefox-0.9.3/f ericfj 26188 26178 0 Nov06 ? 00:00:00 /bin/sh /usr/lib/firefox-0.9.3/r ericfj 26193 26188 0 Nov06 ? 00:07:47 /usr/lib/firefox-0.9.3/firefox-b root 26232 25950 0 Nov06 pts/92 00:00:00 su root 26235 26232 0 Nov06 pts/92 00:00:00 bash ericfj 27112 1 0 Nov06 ? 00:00:00 /usr/bin/artsd -F 10 -S 4096 -s root 27742 1 0 04:08 ? 00:00:00 cupsd ericfj 8585 1 0 07:51 ? 00:01:03 /usr/lib/ooo-1.1/program/soffice ericfj 8604 8585 0 07:51 ? 00:00:00 /usr/lib/ooo-1.1/program/getstyl ericfj 8615 1 0 07:53 ? 00:00:09 gedit file:///home2/ericfj/writi ericfj 9582 1 0 19:22 ? 00:00:03 /usr/bin/esd -terminate -nobeeps ericfj 9621 25962 0 19:37 pts/94 00:00:00 ps -ef
On most modern operating systems, you will find a lot of processes running at any given time.
Note that the options to the ps
command to view all processes are either -ef
or -aux
, depending on the version of Unix. Berkeley-derived versions of Unix such as Mac OS X tend to use -aux
, and System V–based versions of Unix tend to use -ef
.
Linux systems support both types of options.
With Bourne shell scripts, a special variable, $$
, holds the process ID, or PID, of the current process—that is, the process running your script. Note that this process is most likely a running instance of /bin/sh
.
Another special variable, $!
, holds the PID of the last command executed in the background. If your script has not launched any processes in the background, then $!
will be empty.
In addition to the normal process listings, Linux systems support a special file system called /proc. The /proc file system holds information on each running process as well as hardware-related information on your system. The /proc file system started out holding just process information. Now it holds all sorts of operating system and hardware data.
The neat thing about the /proc file system is that it appears to be a normal directory on disk. Inside /proc, you'll find more directories and plain text files, making it easy to write scripts. Each process, for example, has a directory under /proc. The directory name is the process ID number.
The /proc file system holds more than information on processes. It also contains information on devices connected on USB ports, system interrupts, and other hardware-related statuses.
A more interesting process has more open file descriptors. For example:
$ ls -CFl /proc/11751/fd
total 25
lr-x------ 1 ericfj ericfj 64 Nov 8 20:09 0 -> /dev/null
l-wx------ 1 ericfj ericfj 64 Nov 8 20:09 1 -> pipe:[9645622]
lr-x------ 1 ericfj ericfj 64 Nov 8 20:09 10 -> pipe:[9710243]
l-wx------ 1 ericfj ericfj 64 Nov 8 20:09 11 -> pipe:[9710243]
lrwx------ 1 ericfj ericfj 64 Nov 8 20:09 12 -> socket:[9710244]
lrwx------ 1 ericfj ericfj 64 Nov 8 20:09 13 -> socket:[9710250]
lrwx------ 1 ericfj ericfj 64 Nov 8 20:09 14 -> socket:[9710252]
lrwx------ 1 ericfj ericfj 64 Nov 8 20:09 15 -> socket:[9710255]
lrwx------ 1 ericfj ericfj 64 Nov 8 20:09 16 -> socket:[9710260]
lrwx------ 1 ericfj ericfj 64 Nov 8 20:09 17 -> socket:[9710256]
lr-x------ 1 ericfj ericfj 64 Nov 8 20:09 18 -> pipe:[9710402]
lrwx------ 1 ericfj ericfj 64 Nov 8 20:09 19 -> socket:[9710284]
l-wx------ 1 ericfj ericfj 64 Nov 8 20:09 2 -> pipe:[9645622]
l-wx------ 1 ericfj ericfj 64 Nov 8 20:09 20 -> pipe:[9710402]
lr-x------ 1 ericfj ericfj 64 Nov 8 20:09 21 -> pipe:[9710403]
l-wx------ 1 ericfj ericfj 64 Nov 8 20:09 22 -> pipe:[9710403]
lrwx------ 1 ericfj ericfj 64 Nov 8 20:09 23 -> socket:[9710404]
lrwx------ 1 ericfj ericfj 64 Nov 8 20:09 24 -> socket:[9710407]
lrwx------ 1 ericfj ericfj 64 Nov 8 20:09 3 -> socket:[9710237]
lr-x------ 1 ericfj ericfj 64 Nov 8 20:09 4 -> pipe:[9710240]
l-wx------ 1 ericfj ericfj 64 Nov 8 20:09 5 -> pipe:[9710240]
lr-x------ 1 ericfj ericfj 64 Nov 8 20:09 6 -> pipe:[9710241]
l-wx------ 1 ericfj ericfj 64 Nov 8 20:09 7 -> pipe:[9710241]
lr-x------ 1 ericfj ericfj 64 Nov 8 20:09 8 -> pipe:[9710242]
l-wx------ 1 ericfj ericfj 64 Nov 8 20:09 9 -> pipe:[9710242]
This process is using quite a number of network sockets and pipes. This is for a GNOME text editor, gedit
.
Explore the /proc file system to see what you can find. Once you find some interesting files, you can use the cat
command to view the file contents. For example:
$ cat /proc/11751/environ
SSH_AGENT_PID=11135HOSTNAME=kirkwallTERM=dumbSHELL=/bin/bashHISTSIZE=1000QTDIR=/u
sr/lib/qt3.3USER=ericfjLS_COLORS=SSH_AUTH_SOCK=/tmp/sshPwg11087/agent.110
87KDEDIR=/usrPATH=/usr/kerberos/bin:/usr/local/bin:/usr/bin:/bin:/usr/X11R6/b
in:/home2/ericfj/bin:/usr/java/j2sdk1.4.1_03/bin:/opt/jext/binDESKTOP_S ESSION=defaultMAIL=/var/spool/mail/ericfjPWD=/home2/ericfjINPUTRC=/etc/in putrcLANG=en_US.UTF-8GDMSESSION=defaultSSH_ASKPASS=/usr/libexec/openssh/gn ome-ssh-askpassSHLVL=1HOME=/home2/ericfjLOGNAME=ericfjLESSOPEN=|usr/bin/less pipe.sh%sDISPLAY=:0.0G_BROKEN_FILENAMES=1XAUTHORITY=/home2/ericfj/.Xau thorityGTK_RC_FILES=/etc/gtk/gtkrc:/home2/ericfj/.gtkrc-1.2-gnome2SESSION_MANAGER=local/kirkwall:/tmp/.ICE-unix/11087GNOME_KEYRI NG_SOCKET=/tmp/keyring-9XCKKR/socketGNOME_DESKTOP_SESSION_ID=Default
If you stare at this output long enough (with your eyes in a slight squint), you can see a set of environment variables. The problem is that the output is all mashed together.