Jeyhun Karimov
Jeyhun Karimov

Reputation: 1295

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?

Upvotes: 0

Views: 5052

Answers (2)

sterin jacob
sterin jacob

Reputation: 151

grep will show , it's own process . it should be removed using grep -v option

Try like this

for i in ` ps -ef | grep "$pname" | grep -v grep | awk '{print $2}'`
do    
  kill -9 $i
done

Upvotes: 0

Charles Duffy
Charles Duffy

Reputation: 295629

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

Upvotes: 4

Related Questions