Jeyhun Karimov Jeyhun Karimov - 1 year ago 93
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 Source

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 }') )