Bash script kill command in for loop - linux

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?

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

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

Related

before executing script.sh, Check script.sh running or not, if its running kill and continue

CMD# bash script.sh
#!/bin/bash
PRE_CHECK=$0
PROCESS_ID=`ps -ef | grep "$PRE_CHECK" | egrep -v 'grep' | awk '{print $2}'`
[[ ! -z $PROCESS_ID ]] && kill -9 $PROCESS_ID
echo ""
echo ""
echo ""
In order to know this, you need to know your own process ID. This is stored in the variable $$, so you need to kill all PIDs, except for that one.

How does this line of code work in shell?

I got confused about this line of code, could anyone explain how this code work? I can understand that its using a pipeline, but codes in the middle confuses me.
for pid in $(ps -e -f | grep $1 | grep -v $0 | awk '{print $2}')"
Check below details to understand the command, full command will be like this-
cat script.sh
for pid in $(ps -e -f | grep $1 |grep -v $0 | awk '{print $2}')
do
kill -9 $pid
done
You need to execute it like below -
./script.sh vipin
step1 : ps -e -f (will print all the processes running in the server)
step2 : ps -e -f|grep $1 (considering $1 is variable for current user,
in my case is vipin(user) which i will pass with script,so step2
will filter all the process for that user.
step3 : And $0 is the script name (script.sh), which you don't want to kill
that is why you are using grep -v(to exclude)
step4 : awk '{print $2}' to fetch only the process number.

Multiple PIDs being stored in PID file

I have a System V init script I've developed that starts a Java program. For some reason whenever the PID file gets created, it contains multiple PIDs instead of one.
Here's the relevant code that starts the service and writes to the PID file:
daemon --pidfile=$pidfile "$JAVA_CMD &" >> $logfile 2>&1
RETVAL=$?
usleep 500000
if [ $RETVAL -eq 0 ]; then
touch "$lock"
PID=$(ps aux | grep -vE 'grep|runuser|bash' | grep <myservice> | awk '{print $2}')
echo $PID > $pidfile
When I test the ps aux... command manually, a single line returns. When running as a script, it appears that this call is returning multiple PIDs.
Example contents in the PID file: 16601 16602 16609 16619 16690. 16619 is the actual process ID found when manually running the ps aux... command mentioned above.
Try reversing your greps. The first one (-vE) may run BEFORE the myservice one starts up. Grep for your service FIRST, then filter out the unwanted lines:
PID=$(ps aux | grep <myservice> | grep -vE 'grep|runuser|bash' | awk '{print $2}')
I encounted the same issue but not the same statement, it was like this:
PID="$(ps -ef|grep command|grep options|grep -v grep|awk '{print $2}')"
in which I used the same grep order as #Marc said in first answer, but did not filter all the unwanted lines.
So I tried the below one and it worked:
PID="$(ps -ef|grep command|grep options|grep -vE 'grep|runuser|bash'|awk '{print $2}')"

xargs' $1 conflicts with $1 in shell script

I have these lines in one shell script file foo.sh:
ps ax | grep -E "bar" | grep -v "grep" | awk '{print $1}' | xargs kill -9 $1
when I execute the shell script with an arguments like this:
sh foo.sh arg_one
the xargs can't work now. It takes the $1 from the shell script but not the output of awk.
I do know I can store the output of awk into one file and use it in xargs later.
But, is there any better solution?
== edited ==
thanks the answer from #peterph.
But, is there any way that I can use $1 in xargs?
== edited 2 ==
thanks #Brian Campbell
Despite weather there should be a useless $1 in the example, if a argument of "the shell script file" is given, then the $1 in xargs will not work as my wish, in my computer(In your computer too, I think).
Why? And, how to get avoid it?
xargs reads list from stdin so just discard the last $1 on the line if what you want is to kill processes by their PIDs.
As a side note, ps can also print processes according to their command name (with procps on linux see the -C option).
Instead of that complicated pipeline, you can always use killall -9 name to kill a process, or pkill -9 pattern if you don't know the exact name of the process but know a substring (be careful that you don't kill any unintended processes, though).
For your command to work, just remove the $1; xargs takes its arguments from standard in, and runs the command line passing in the values it gets from standard in at the end of the command.
edit (in response to your edit): What do you expect xargs to do with the $1 argument? What are you expecting to be in it? The only interpretation of $1 that has any meaning here is the first argument that was passed to your script.
The $1 from your awk script is what awk finds in the first column of its input; it then prints that out, and xargs takes those values from standard input, and will call the command you pass it with those values at the end of the command line. So if the awk command returns:
100
120
130
Then piping that result to xargs kill -9 will result in the following being called:
kill -9 100 120 130
You do not need a variable like $1 to make this work
This should work:
ps ax | grep -E "bar" | grep -v "grep" | awk '{print $1}' | xargs kill -9
You can also try:
result=$(ps -ef | grep -E "bar" | grep -v "grep" | awk '{print $2}')
kill -9 $result
In my case piping xargs sometimes returned below error even if matched processes existed:
usage: kill [ -s signal | -p ] [ -a ] pid ...
kill -l [ signal ]
usage: kill [ -s signal | -p ] [ -a ] pid ...
kill -l [ signal ]

Kill random process with name

I want a way to kill a random process with a name (eg a random perl process).
What would be the best way of doing this?
I was thinkign of using something like this:
ps aux | grep PROCESS-NAME
to a file, then find a random line number, get the second column (process ID?) and kill that.
For my use it doesn't actually need to be a random one, as long as it kills one of the processes. Making it random just makes it better.
look at the -r option of the killall command!
Bash one-liner :-p
kill `ps auxww | grep zsh | awk '{print $2}' | while read line; do echo "$RANDOM $line"; done | sort | cut -d ' ' -f 2 | head -n 1`
There's also the 'pidof' command, which can be used to kill with:
kill `pidof processname`
To get just one process when there are multiple with the same name, use -s for "single shot".
It sounded like you were already on the right track.
you can use the following perl script, save it as randomline.pl, which will return a random line from whats piped into it
#!/usr/bin/perl
srand (time ^ $$ ^ unpack "%L*", `ps axww | gzip`);
while (<>) { push(#_,$_); } print #_[rand()*#_];
then run the following command to send the kill command
kill `ps aux | grep PROCESS-NAME | perl randomline.pl | awk '{print $2}'`
You might also want to add in some checking, perhaps with an inverted grep for root to make sure you don't try to kill root level processes that match your process name.
just kill and awk.
kill $(ps -eo cmd,pid|awk '/zsh/&&!/awk/{pid[$NF]}END{for(i in pid){print i;exit}}')
the for loop in the END block will give you you a random pid to kill
with recent bash shell
#!/bin/bash
declare -a pid
pid=( $(pidof myprocess) )
length=${#pid}
rnumber=$((RANDOM%length+1))
rand=$((rnumber-1))
kill ${pid[$rand]}
How about using pgrep and pkill. They allow lot of options to select the processes.
kill process with name "my_proc_name" :
kill -9 `ps xf | grep my_proc_name | grep -v grep | cut -d " " -f 1`
Maybe off topic, but I use this on Cygwin. Inspired by
Lev Victorovich Priyma’s answer
ps -W | awk '/calc.exe/,NF=1' | xargs kill -f
or
ps -W | awk '$0~z,NF=1' z=calc.exe | xargs kill -f

Resources