Jeyhun Karimov Jeyhun Karimov - 4 months ago 26
Bash Question

Bash script kill command in for loop

I want to kill all processes containing some string. I wrote script for doing this. However, when I execute it, it gets "Killed" signal after first iteration of for loop. This is my code:

#!/bin/bash

executeCommand () {
local pname="$1";
echo $HOSTNAME;
local search_terms=($(ps aux | grep $pname | awk '{print $2}'))
for pros in "${search_terms[@]}"; do
kill -9 "$pros"
echo $pros
done
exit
}

executeCommand "$1" # get the string that process to be killed contains


I execute it like
./my_script.sh zookeeper
.
When I delete the line containing
kill
command,
for loop
executes until end, otherwise, after first
kill
command, I get as an output "
Killed
" and program exits.

What is possible reason for this, and any other solution to reach my goal?

Answer

The silly (faulty, buggy) way to do this is to add grep -v grep to your pipeline:

# ${0##*/} expands to the name of the running script
# ...thus, we avoid killing either grep, or the script itself
ps aux | grep -e "$pname" | egrep -v "grep|${0##*/}" | awk '{print $2}'

The better way is to use a tool built for the job:

# pkill already, automatically, avoids killing any of its parent processes
pkill "$pname"

That said, matching processes by name is a bad practice to start with -- you'll also kill less yourproc.log or vim yourproc.conf, not just yourproc. Don't do it; instead, use a proper process supervision system (upstart, DJB daemontools, Apple launchd, systemd, etc) to monitor your long-running daemons and kill or restart them when needed.


By the way -- there's no need for a for loop at all: kill can be passed multiple PIDs on a single invocation, like so:

# a bit longer and bash-specific, but avoids globbing
IFS=$'\n' read -r -d '' -a pids \
  < <(ps auxw | awk -v proc="$pname" -v preserve="${0##*/}" \
      '$0 ~ proc && $0 !~ preserve && ! /awk/ { print $2 }' \
      && printf '\0')
kill -- "${pids[@]}"

...which could also be formulated as something like:

# setting IFS and running `set -f` necessary to make unquoted expansion safe
( IFS=$'\n'; set -f; exec kill -- \
  $(ps auxw | awk -v proc="$pname" -v preserve="${0##*/}" \
    '$0 ~ proc && $0 !~ preserve && ! /awk/ { print $2 }') )