52 Killing Processes by Name


#52 Killing Processes by Name

Linux and some Unixes have a very helpful command called killall , which allows you to kill all running applications that match a specified pattern. It can be quite helpful when you want to kill nine mingetty daemons, or even just to send a SIGHUP signal to xinetd to prompt it to reread its configuration file. Systems that don't have killall can emulate it in a shell script, built around ps for identification of matching processes and kill to send the specified signal.

The tricky part of the script is that the output format from ps varies significantly from OS to OS. For example, consider how differently Mac OS X and Red Hat Linux show running processes in the default ps output:

 OSX $  ps  PID  TT  STAT       TIME COMMAND   485 std  S       0:00.86 -bash (bash)   581  p2  S       0:00.01 -bash (bash) RHL9 $  ps  PID TTY           TIME CMD  8065 pts/4     00:00:00 bash 12619 pts/4     00:00:00 ps 

Worse, rather than model its ps command after a typical Unix command, the GNU ps command accepts BSD-style flags, SYSV-style flags, and GNU-style flags. A complete mishmash!

Fortunately, some of these inconsistencies can be sidestepped in this particular script by using the -cu flag, which produces consistent output that includes the owner of the process, the command name (as opposed to -bash (bash) , as in the default Mac OS X output just shown), and the process ID, the lattermost of which is what we're really interested in identifying.

The Code

 #!/bin/sh # killall - Sends the specified signal to all processes that match a #   specific process name. # By default it only kills processes owned by the same user, unless #   you're root. Use -s SIGNAL to specify a signal to send to the process, #   -u user to specify the user, -t tty to specify a tty, #   and -n to only report what'd be done, rather than doing it. signal="-INT"   # default signal user=""   tty=""   donothing=0 while getopts "s:u:t:n" opt; do   case "$opt" in         # Note the trick below: kill wants -SIGNAL but we're asking         # for SIGNAL so we slip the '-' in as part of the assignment     s ) signal="-$OPTARG";               ;;     u ) if [ ! -z "$tty" ] ; then            echo " 
 #!/bin/sh # killall - Sends the specified signal to all processes that match a # specific process name. # By default it only kills processes owned by the same user, unless # you're root. Use -s SIGNAL to specify a signal to send to the process, # -u user to specify the user, -t tty to specify a tty, # and -n to only report what'd be done, rather than doing it. signal="-INT" # default signal user="" tty="" donothing=0 while getopts "s:u:t:n" opt; do case "$opt" in # Note the trick below: kill wants -SIGNAL but we're asking # for SIGNAL so we slip the '-' in as part of the assignment s ) signal="-$OPTARG"; ;; u ) if [ ! -z "$tty" ] ; then echo "$0: error: -u and -t are mutually exclusive." >&2 exit 1 fi user=$OPTARG; ;; t ) if [ ! -z "$ user " ] ; then echo "$0: error: -u and -t are mutually exclusive." >&2 exit 1 fi tty=$2; ;; n ) donothing=1; ;; ? ) echo "Usage: $0 [-s signal] [-u user-t tty] [-n] pattern" >&2 exit 1 esac done shift $(( $OPTIND - 1 )) if [ $# -eq 0 ] ; then echo "Usage: $0 [-s signal] [-u user-t tty] [-n] pattern" >&2 exit 1 fi if [ ! -z "$tty" ] ; then pids=$(ps cu -t $tty  awk "/ $1$/ { print \$2 }") elif [ ! -z "$user" ] ; then pids=$(ps cu -U $user  awk "/ $1$/ { print \$2 }") else pids=$(ps cu -U ${USER:-LOGNAME}  awk "/ $1$/ { print \$2 }") fi if [ -z "$pids" ] ; then echo "$0: no processes match pattern $1" >&2; exit 1 fi for pid in $pids do # Sending signal $signal to process id $pid: kill might # still complain if the process has finished, the user doesn't # have permission, etc., but that's okay. if [ $donothing -eq 1 ] ; then echo "kill $signal $pid" else kill $signal $pid fi done exit 0 
: error: -u and -t are mutually exclusive." >&2 exit 1 fi user=$OPTARG; ;; t ) if [ ! -z "$user" ] ; then echo "
 #!/bin/sh # killall - Sends the specified signal to all processes that match a # specific process name. # By default it only kills processes owned by the same user, unless # you're root. Use -s SIGNAL to specify a signal to send to the process, # -u user to specify the user, -t tty to specify a tty, # and -n to only report what'd be done, rather than doing it. signal="-INT" # default signal user="" tty="" donothing=0 while getopts "s:u:t:n" opt; do case "$opt" in # Note the trick below: kill wants -SIGNAL but we're asking # for SIGNAL so we slip the '-' in as part of the assignment s ) signal="-$OPTARG"; ;; u ) if [ ! -z "$tty" ] ; then echo "$0: error: -u and -t are mutually exclusive." >&2 exit 1 fi user=$OPTARG; ;; t ) if [ ! -z "$ user " ] ; then echo "$0: error: -u and -t are mutually exclusive." >&2 exit 1 fi tty=$2; ;; n ) donothing=1; ;; ? ) echo "Usage: $0 [-s signal] [-u user-t tty] [-n] pattern" >&2 exit 1 esac done shift $(( $OPTIND - 1 )) if [ $# -eq 0 ] ; then echo "Usage: $0 [-s signal] [-u user-t tty] [-n] pattern" >&2 exit 1 fi if [ ! -z "$tty" ] ; then pids=$(ps cu -t $tty  awk "/ $1$/ { print \$2 }") elif [ ! -z "$user" ] ; then pids=$(ps cu -U $user  awk "/ $1$/ { print \$2 }") else pids=$(ps cu -U ${USER:-LOGNAME}  awk "/ $1$/ { print \$2 }") fi if [ -z "$pids" ] ; then echo "$0: no processes match pattern $1" >&2; exit 1 fi for pid in $pids do # Sending signal $signal to process id $pid: kill might # still complain if the process has finished, the user doesn't # have permission, etc., but that's okay. if [ $donothing -eq 1 ] ; then echo "kill $signal $pid" else kill $signal $pid fi done exit 0 
: error: -u and -t are mutually exclusive." >&2 exit 1 fi tty=; ;; n ) donothing=1; ;; ? ) echo "Usage:
 #!/bin/sh # killall - Sends the specified signal to all processes that match a # specific process name. # By default it only kills processes owned by the same user, unless # you're root. Use -s SIGNAL to specify a signal to send to the process, # -u user to specify the user, -t tty to specify a tty, # and -n to only report what'd be done, rather than doing it. signal="-INT" # default signal user="" tty="" donothing=0 while getopts "s:u:t:n" opt; do case "$opt" in # Note the trick below: kill wants -SIGNAL but we're asking # for SIGNAL so we slip the '-' in as part of the assignment s ) signal="-$OPTARG"; ;; u ) if [ ! -z "$tty" ] ; then echo "$0: error: -u and -t are mutually exclusive." >&2 exit 1 fi user=$OPTARG; ;; t ) if [ ! -z "$ user " ] ; then echo "$0: error: -u and -t are mutually exclusive." >&2 exit 1 fi tty=$2; ;; n ) donothing=1; ;; ? ) echo "Usage: $0 [-s signal] [-u user-t tty] [-n] pattern" >&2 exit 1 esac done shift $(( $OPTIND - 1 )) if [ $# -eq 0 ] ; then echo "Usage: $0 [-s signal] [-u user-t tty] [-n] pattern" >&2 exit 1 fi if [ ! -z "$tty" ] ; then pids=$(ps cu -t $tty  awk "/ $1$/ { print \$2 }") elif [ ! -z "$user" ] ; then pids=$(ps cu -U $user  awk "/ $1$/ { print \$2 }") else pids=$(ps cu -U ${USER:-LOGNAME}  awk "/ $1$/ { print \$2 }") fi if [ -z "$pids" ] ; then echo "$0: no processes match pattern $1" >&2; exit 1 fi for pid in $pids do # Sending signal $signal to process id $pid: kill might # still complain if the process has finished, the user doesn't # have permission, etc., but that's okay. if [ $donothing -eq 1 ] ; then echo "kill $signal $pid" else kill $signal $pid fi done exit 0 
[-s signal] [-u user-t tty] [-n] pattern" >&2 exit 1 esac done shift $(( $OPTIND - 1 )) if [ $# -eq 0 ] ; then echo "Usage:
 #!/bin/sh # killall - Sends the specified signal to all processes that match a # specific process name. # By default it only kills processes owned by the same user, unless # you're root. Use -s SIGNAL to specify a signal to send to the process, # -u user to specify the user, -t tty to specify a tty, # and -n to only report what'd be done, rather than doing it. signal="-INT" # default signal user="" tty="" donothing=0 while getopts "s:u:t:n" opt; do case "$opt" in # Note the trick below: kill wants -SIGNAL but we're asking # for SIGNAL so we slip the '-' in as part of the assignment s ) signal="-$OPTARG"; ;; u ) if [ ! -z "$tty" ] ; then echo "$0: error: -u and -t are mutually exclusive." >&2 exit 1 fi user=$OPTARG; ;; t ) if [ ! -z "$ user " ] ; then echo "$0: error: -u and -t are mutually exclusive." >&2 exit 1 fi tty=$2; ;; n ) donothing=1; ;; ? ) echo "Usage: $0 [-s signal] [-u user-t tty] [-n] pattern" >&2 exit 1 esac done shift $(( $OPTIND - 1 )) if [ $# -eq 0 ] ; then echo "Usage: $0 [-s signal] [-u user-t tty] [-n] pattern" >&2 exit 1 fi if [ ! -z "$tty" ] ; then pids=$(ps cu -t $tty  awk "/ $1$/ { print \$2 }") elif [ ! -z "$user" ] ; then pids=$(ps cu -U $user  awk "/ $1$/ { print \$2 }") else pids=$(ps cu -U ${USER:-LOGNAME}  awk "/ $1$/ { print \$2 }") fi if [ -z "$pids" ] ; then echo "$0: no processes match pattern $1" >&2; exit 1 fi for pid in $pids do # Sending signal $signal to process id $pid: kill might # still complain if the process has finished, the user doesn't # have permission, etc., but that's okay. if [ $donothing -eq 1 ] ; then echo "kill $signal $pid" else kill $signal $pid fi done exit 0 
[-s signal] [-u user-t tty] [-n] pattern" >&2 exit 1 fi if [ ! -z "$tty" ] ; then pids=$(ps cu -t $tty awk "/ $/ { print $2 }") elif [ ! -z "$user" ] ; then pids=$(ps cu -U $user awk "/ $/ { print $2 }") else pids=$(ps cu -U ${USER:-LOGNAME} awk "/ $/ { print $2 }") fi if [ -z "$pids" ] ; then echo "
 #!/bin/sh # killall - Sends the specified signal to all processes that match a # specific process name. # By default it only kills processes owned by the same user, unless # you're root. Use -s SIGNAL to specify a signal to send to the process, # -u user to specify the user, -t tty to specify a tty, # and -n to only report what'd be done, rather than doing it. signal="-INT" # default signal user="" tty="" donothing=0 while getopts "s:u:t:n" opt; do case "$opt" in # Note the trick below: kill wants -SIGNAL but we're asking # for SIGNAL so we slip the '-' in as part of the assignment s ) signal="-$OPTARG"; ;; u ) if [ ! -z "$tty" ] ; then echo "$0: error: -u and -t are mutually exclusive." >&2 exit 1 fi user=$OPTARG; ;; t ) if [ ! -z "$ user " ] ; then echo "$0: error: -u and -t are mutually exclusive." >&2 exit 1 fi tty=$2; ;; n ) donothing=1; ;; ? ) echo "Usage: $0 [-s signal] [-u user-t tty] [-n] pattern" >&2 exit 1 esac done shift $(( $OPTIND - 1 )) if [ $# -eq 0 ] ; then echo "Usage: $0 [-s signal] [-u user-t tty] [-n] pattern" >&2 exit 1 fi if [ ! -z "$tty" ] ; then pids=$(ps cu -t $tty  awk "/ $1$/ { print \$2 }") elif [ ! -z "$user" ] ; then pids=$(ps cu -U $user  awk "/ $1$/ { print \$2 }") else pids=$(ps cu -U ${USER:-LOGNAME}  awk "/ $1$/ { print \$2 }") fi if [ -z "$pids" ] ; then echo "$0: no processes match pattern $1" >&2; exit 1 fi for pid in $pids do # Sending signal $signal to process id $pid: kill might # still complain if the process has finished, the user doesn't # have permission, etc., but that's okay. if [ $donothing -eq 1 ] ; then echo "kill $signal $pid" else kill $signal $pid fi done exit 0 
: no processes match pattern " >&2; exit 1 fi for pid in $pids do # Sending signal $signal to process id $pid: kill might # still complain if the process has finished, the user doesn't # have permission, etc., but that's okay. if [ $donothing -eq 1 ] ; then echo "kill $signal $pid" else kill $signal $pid fi done exit 0

How It Works

Because this script is so aggressive , I've put some effort into minimizing false pattern matches, so that a pattern like sh won't match output from ps that contains bash or vi crashtest.c , or other values that embed the pattern. This is done by the pattern-match prefix on the awk command:

 awk "/ $/ { print $2 }" 

Left- rooting the specified pattern, $1 , with a leading space and right-rooting the pattern with a trailing $ , causes the script to search for the specified pattern 'sh' in ps output as ' sh$' .

Running the Script

This script has a variety of starting flags that let you modify its behavior. The -s signal flag allows you to specify a signal other than the default interrupt signal, SIGINT , to send to the matching process or processes. The -u user and -t tty flags are useful primarily to the root user in killing all processes associated with a specified user or TTY device, respectively. And the -n flag gives you the option of having the script report what it would do without actually sending any signals. Finally, a pattern must be specified.

The Results

To kill all the csmount processes on my Mac OS X system, I can now use the following:

 $  ./killall -n csmount  kill -INT 1292 kill -INT 1296 kill -INT 1306 kill -INT 1310 kill -INT 1318 

Hacking the Script

There's an unlikely , though not impossible , bug in this script. To match only the specified pattern, the awk invocation outputs the process ID only of processes that match the pattern plus a leading space that occurs at the end of the input line. However, it's theoretically possible to have two processes running, one called, say, bash and the other emulate bash . If killall is invoked with bash as the pattern, both of these processes will be matched, although only the former is a true match. Solving this to give consistent cross-platform results would prove quite tricky.

If you're motivated, you could also write a script based heavily on the killall script that would let you renice jobs by name, rather than just by process ID. The only change required would be to invoke renice rather than kill .




Wicked Cool Shell Scripts. 101 Scripts for Linux, Mac OS X, and Unix Systems
Wicked Cool Shell Scripts
ISBN: 1593270127
EAN: 2147483647
Year: 2004
Pages: 150
Authors: Dave Taylor

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net