Count the number of processes and kill them - bash

I am writing a script to kill all instances of the same process. As it is going to be used on Linux, AIX, HP-UX and Solaris, I need to use only built-in bash (sh) functions. That's why killall, pkill, etc. don't work for me.
Once there is only one instance of a process it should be just killed in traditional way:
kill -TERM `ps -ef | grep -v grep | grep $process | awk '{print $2}'`
However sometimes the program has extra instances running and that's why ps -ef | … returns more than one PID. That needs to be reported.
example:
bash-3.2$ ps -ef | grep -v grep | grep perl | awk '{print $2}'
5267
5268
5269
5270
5271
My thought was to store those values in a temporary variable and then send kill signal to each in a for loop.
bash-3.2$ tmp=`ps -ef | grep -v grep | grep perl | awk '{print $2}'`
bash-3.2$ echo $tmp
5267 5268 5269 5270 5271
However I still need the information if such a case occurred (how many instances were present).
It seems I need to check the whole string stored in the tmp variable and maybe count spaces?
Anyway the questions reduces to how to check how many values the $tmp variable stores?

For maximum portability and reliability, use -A (POSIX synonym of -e) and a custom format with -o rather than -f.
Your filtering of the output of ps is brittle: it may match other processes. You've had to exclude the grep process, and you may need to exclude your script as well, and there may be other completely innocent processes caught in the fray (such as your script itself) because their command line happens to contain $process as a substring. Make your filtering as strict as possible. With ps -o pid= -o comm=, you get just two columns (PID and command without arguments) with no header.
You don't need to use a loop to do the killing, kill accepts multiple arguments. For the counting, let the shell do it: you have a whitespace-separated list of numbers, to let the shell do the word splitting (with $(…) outside quotes) and count the number of resulting words ($#).
count_and_kill_processes () {
set -- $(ps -A -o pid= -o comm= |
awk -v "name=$process" '$2 == name {print $1}')
count=$#
if [ $# -ne 0 ]; then kill "$#"; fi
}
count_and_kill_processes foo
# now the number of killed processes is in $count
If your shell is bash or ksh on all machines, you can use an array.
pids=($(ps -A -o pid= -o comm= |
awk -v "name=$process" '$2 == name {print $1}') )
if [[ $# -ne 0 ]]; then kill "$#"; fi
# the number of killed processes is ${#pids}

use xargs:
ps aux | grep -ie perl | awk '{print $2}' | xargs kill -9

You can use a loop which should work in both cases:
for pid in $(ps -ef | grep -v grep | grep $process | awk '{print $2}'); do
echo $pid
done
Or count the number of matches:
if [ $(ps -ef | grep -v grep | grep $process | awk '{print $2}' | wc -l) -gt 1 ]
then
# more than one
fi

Related

Killing parent process only, not both child and parent in bash

I'm trying to kill process by name. It's supposed to kill children processes first then the parent later but I only get parent killed. Help needed please. EDIT: SOLVED
Please try it this way:
PID=$(ps -aef | grep `whoami` | grep $argument | grep -v grep | grep -v $$ | awk '{print $2}'
for x in ${PID[#]}; do
CPID=$(ps -aef | grep `whoami` | grep $PID | grep -v grep | grep -v $$ | awk '{print $3}'
for y in ${CPID[#]}; do
kill $y
done
kill $x
done

How do I kill all processes running from a particular directory?

I'm looking for a quick terminal command that will kill all of the processes which are running from a particular directory (or a subdirectory of that directory).
For example, let's say I have the bin1 and bin2 executables running. They live at the following paths:
/path/to/processes/subdir1/bin1
/path/to/processes/subdir2/subsubdir2/bin2
I want to kill both bin1 and bin2 by only specifying /path/to/processes such that the command will find and kill both bin1 and bin2 because of their location.
Old question I know but I came across this looking for an answer and eventually found one. I don't know if this is the "best" way to do this or not but you gotta start somewhere and I've found this to be very reliable:
ps -eo pid | while read line; do pwdx $line 2> /dev/null; done | grep "your/path/here" | cut -d':' -f1 | while read line; do kill $line; done;
ps -eo pid: lists all process ids
while read line; do pwdx $line 2> /dev/null; done: gets more info about each process, including the directory it's running from
2> /dev/null: removes error lines about processes you don't have permissions to (optional)
cut -d':' -f1 extracts only the pids
while read line; do kill $line; done;: kills each process by pid
I use this command to kill process in a specific directory:
lsof | grep '\/path\/to\/processes\/' | awk '{print $2}' | xargs kill
One way would be to say:
ps --no-headers axk comm o pid,args | awk '$2 ~ "/path/to/processes/"{print $1}' | xargs kill -9

all users executing a certain command. grep

I'd like to find all the names of the users that are executing a certain command given as a parameter.
grep must be used.
I have tried: ps aux | grep $1 | cut -d" " -f1, but it's not the desired result.
/usr/ucb/ps aux | awk '/<your_command_as_parameter>/{print $1}'|sort -u
for eg:
> /usr/ucb/ps aux | awk '/rlogin/{print $1}' | sort -u
I guess you're looking for this.
# cat test.sh
ps aux | grep $1 | grep -v grep | awk '{print $1}'
# ./test.sh bash
root
root
root
There is a trick to getting the information for processes but not the process that's searching for the process, which is to make the name into a regex. For example, if you're searching for ls, make the search term into grep '[l]s'. This works unless you're searching for grep itself, or a single-letter command name.
This is the procname script I use; it works with most POSIX shells:
#! /bin/ksh
#
# #(#)$Id: procname.sh,v 1.3 2008/12/16 07:25:10 jleffler Exp $
#
# List processes with given name, avoiding the search program itself.
#
# If you ask it to list 'ps', it will list the ps used as part of this
# script; if you ask it to list 'grep', it will list the grep used as
# part of this process. There isn't a sensible way to avoid this. On
# the other hand, if you ask it to list httpd, it won't list the grep
# for httpd. Beware metacharacters in the first position of the
# process name.
case "$#" in
1)
x=$(expr "$1" : '\(.\).*')
y=$(expr "$1" : '.\(.*\)')
ps -ef | grep "[$x]$y"
;;
*)
echo "Usage: $0 process" 1>&2
exit 1
;;
esac
In bash, you could use the variable substringing operations to avoid the expr commands:
case "$#" in
1) ps -ef | grep "[${1:0:1}]${1:1}"
;;
*)
echo "Usage: $0 process" 1>&2
exit 1
;;
esac
Both of these run ps -ef; you can use ps aux if you prefer. The search for the 'command' name is not constrained to the command portion of the command, so you could use procname root to find processes run by root. The match is also not constrained to a full word; you could consider grep -w for that (a GNU grep extension).
The output of these is the full line of data from ps; if you want just the user (the first field), then pipe the output to awk '{print $1}' | sort -u.

No command executed after performing kill command in shell script

Here is my shell script:
#!/bin/bash
PIDS=$(ps -e | grep $1 |grep -v grep| awk '{print $1}')
kill -s SIGINT $PIDS
echo "Done sendings signal"
I am passing the name of the process as command line argument.
The echo command is not getting executed, although the target processes are actually receiving the SIGINT signal and exited.
Any suggestions?
Update:
I changed the code to:
#!/bin/bash
PIDS=$(ps -e |grep $1 | grep -v grep | awk '{print $1}'|grep -v $$)
echo $PIDS
kill -s SIGINT $PIDS
echo "Done sendings signal"
echo "The current process is $$"
Now I am noticing a strange thing:
The script is working but not as expected. Executing following command in command line outside the script
ps -e|grep process-name|grep -v grep|awk '{print $1}'|grep -v $$
gives pid of the process-name but when I execute the same command inside shell script, assign it to variable PIDS and then echo PIDS then it shows one more pid in addition to the pid of process-name. Therefore when the kill command executes it gives an error that the process with second pid doesn't exist. It does echo the remaining sentences in the terminal. Any clue ?
There really are only a couple of possibilities. Assuming you're just running this from the command line, you should see the message ... unless, of course, what you're doing puts the PID of your shell process in PIDS, in which case the kill would kill the (sub) shell running your command before you hit the echo.
Suggestion: echo $PIDS before you call kill and see what's there. In fact, I'd be tempted to comment out the kill and try the command, just to see what happens.
#!/bin/bash
PIDS=$(ps -e | grep $1 |grep -v grep| awk '{print $1}')
echo $PIDS
# kill -s SIGINT $PIDS
echo "Done sendings signal"
Of course, you can always run the script with bash -x to see everything.
Your script works. The only reason I can see for the echo not being executed is that some value of $1 and the script file name combine so that your script PID is also gathered, thereby making the script suicide.
The PIDS line spawns a process running ps, grep, another grep -- so you won't find in PIDS the processes running grep, but what about the parent process itself?
Try:
#!/bin/bash
PIDS=$(ps -e | grep $1 |grep -v grep | awk '{print $1}' | grep -v "^$$\$" )
kill -s SIGINT $PIDS
echo "Done sendings signal"
or run the pipes one after the other with suitable safety greps.
Edit: it is evident that the "$1" selection is selecting too much. So I'd rewrite the script like this:
#!/bin/bash
# Gather the output of "ps -e". This will also gather the PIDs of this
# process and of ps process and its subshell.
PSS=$( ps -e )
# Extract PIDs, excluding this one PID and excluding a process called "ps".
# Don't need to expunge 'grep' since no grep was running when getting PSS.
PIDS=$( echo "$PSS" | grep -v "\<ps\>" | grep "$1" | awk '{print $1}' | grep -v "^$$\$" )
if [ -n "$PIDS" ]; then
kill -s SIGINT $PIDS
else
echo "No process found matching $1"
fi
echo "Done sending signal."
ps -e is identical to ps -A and selects all processes ( cf. http://linux.die.net/man/1/ps ), i. e. ps -e displays "information about other users' processes, including those without controlling terminals" (Mac OS X man page of ps). This means you will also kill the PID ($$) of your shell process, as already pointed out by Charlie Martin, because you will also grep a line of output of the ps -e command that looks like so:
67988 ttys000 0:00.00 /bin/bash ./killpids sleep
Just log the output of ps -e to a file to see that your script commits suicide:
./killpids sleep 2>err.log
#!/bin/bash
# cat killpids
echo $$
for n in {1..10}; do
sleep 5000 &
done
sleep 1
unset PIDS
PIDS="$(ps -e | tee /dev/stderr | grep "$1" | grep -v grep | awk '{print $1}')"
#PIDS="$(ps -www -U $USER -o pid,uid,comm | tee /dev/stderr | grep "$1" | grep -v grep | awk '{print $1}')"
wc -l <<<"$PIDS"
#kill -s SIGINT $PIDS
echo kill -s TERM $PIDS
kill -s TERM $PIDS
echo "Done sendings signal"

Shell script to kill a process

I need to implement a shell script that kills a process. The problem is that I need to do a conditional to be able to see if the process is running or not.
This is my code, but it is not working:
#!/bin/sh
if [ -x "MY_PROCCESS_NAME"]; then
killall MY_PROCCESS_NAME
else
echo "Doesn't exist"
fi
This is the error:
line 3: [: missing `]'
to check if a process is running on mac os x you can use:
pid=$(ps -fe | grep 'process name' | grep -v grep | awk '{print $2}')
if you want to reduce the number of shell scripts you can enclose one of the characters of the name of the process in square brackets:
pid=$(ps -fe | grep '[p]rocess name' | awk '{print $2}')
combined in your test this would look like:
pid=$(ps -fe | grep '[p]rocess name' | awk '{print $2}')
if [[ -n $pid ]]; then
kill $pid
else
echo "Does not exist"
fi
it's a little more complicated than you would need to do under linux as you generally have the 'pgrep' command, which is the rough equivalent of the 'ps -fe | grep ... | grep -v grep'
not sure if it would work in OSX, it works in ubuntu.
but as a one liner:
ps aux | awk '$11~/vim/ {PID = $2} END {if (PID) print "kill -9 "PID; else print "echo no process"}' | bash
what it does is it finds a process, in this case, vim and returns the kill -9 pid if no string is found it returns echo no process it then pipes the output to bash.

Resources