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.
Related
I'm trying:
#!/bin/bash
if $(ps -C "bm_d21_debug")
then
kill $(ps -C "bm_d21_debug" -o pid=)
echo "exists"
fi
It returns: "PID: command not found"
Not sure what I'm doing wrong?
Consider this line:
if $(ps -C "bm_d21_debug")
You execute the ps command in a command substitution, which returns the command output. The if command then tries to run that output as a command.
The first word of the ps output is PID, which if will handle as the command name. Thus, the "command not found" error.
You just want
if ps -C "bm_d21_debug" >/dev/null; then
echo running
else
echo NOT running
fi
I suggest to use square brackets also:
if [[ $(ps -C "bm_d21_debug") ]]
But this command will always return "yes" ($? = 0)
Fixed by changing to
if ps aux | grep ./bm_d21_debug | grep -v grep >/dev/null;then
pid=$(ps aux | grep ./bm_d21_debug | grep -v grep | awk '{print $2}')
kill $pid
echo $pid
fi
When trying to run the following command over ssh:
ssh hostname 'for pid in $(ps -ef | grep "some process" | awk '{print $2}'); do kill -9 $pid; done'
I get the following error:
awk: cmd. line:1: {print
awk: cmd. line:1: ^ unexpected newline or end of string
I've tried escaping in different ways but haven't found the correct way - or maybe it's something else?
Thanks in advance!
You cannot include single quotes into a single-quoted string, since nothing is interpreted inside, except the single quote that closes the string. You can concatenate the single-quoted strings with "'":
ssh host 'for pid in $(ps -ef | grep "some process" | awk '"'"'{print $2}'"'"'); do kill -9 $pid; done'
Alternatively, concatenate escaped single quotes (\'):
ssh host 'for pid in $(ps -ef | grep "some process" | awk '\''{print $2}'\''); do kill -9 $pid; done'
See Strong Quoting.
The Cause of the Error
Your command is interpreted as a couple of $IFS-separated arguments:
for pid in $(ps -ef | grep "some process" | awk {print
}); do kill -9 $pid; done
There is no $2 in the strings, since $2 is interpreted as a shell variable, and the value of this variable in the normal shell context is empty.
Thus, you have sent the following command to the server:
for pid in $(ps -ef | grep "some process" | awk {print }); do kill -9 $pid; done
If you run this command in a terminal, you will get the same error from AWK.
This question already has answers here:
How to kill all processes with a given partial name? [closed]
(14 answers)
Closed 6 years ago.
Right now, my bash script works for 1 PID processes and I must use an exact process name for input. It will not accept *firefox*' for example. Also, I run a bash script that opens multiplersync` processes, and I would like this script to kill all of those processes. But, this script only works on processes with 1 PID.
Here is the script:
#!/bin/bash
createProcfile() {
ps -eLf | grep -f process.tmp | grep -v 'grep' | awk '{print $2,$10}' | sort -u | egrep -o '[0-9]{4,}' > pid.tmp
# pgrep "$(cat process.tmp)" > pid.tmp
}
PIDFile=pid.tmp
echo "Enter a process name"
read -r process
echo "$process" > process.tmp
# node_process_id=$(pidof "$process")
node_process_id=$(ps -eLf | grep $process | grep -v 'grep' | awk '{print $2,$10}' | sort -u | egrep -o '[0-9]{4,}')
if [[ -z "$node_process_id" ]]; then
echo "Please enter a valid process."
rm process.tmp
exit 0
fi
ps -eLf | grep $process | awk '{print $2,$10}' | sort -u | grep -v 'grep'
# pgrep "$(cat process.tmp)"
echo "Would you like to kill this process(es)? (y/n)"
read -r answer
if [[ "$answer" == y ]]; then
createProcfile
pkill -F "$PIDFile"
rm "$PIDFile"
sleep 1
createProcfile
node_process_id=$(pidof "$process")
if [[ -z $node_process_id ]]; then
echo "Process terminated successfully."
rm process.tmp
exit 0
else
echo "Process not terminated. Kill process manually."
ps -eLf | grep $process | awk '{print $2,$10}' | sort -u | grep -v 'grep'
# pgrep "$(cat process.tmp)"
rm "$PIDFile"
rm process.tmp
exit 0
fi
fi
I edited the script. Thanks to your comments, it works now and does the following:
Make script accept partial name as input
Kill more than 1 PID
Thank you!
pkill exists to solve your problem. It accepts a pattern to match against the process name, or the entire command line if -f is specified.
It will not accept *firefox*
Use killall command. Example :
killall -r "process.*"
This will kill all the processes whose names contain process in the beginning followed by any stuff.
The [ manual ] says :
-r, --regexp
Interpret process name pattern as an extended regular expression.
Sidenote:
Note that we have to double quote the regular expression to prevent file globbing. (Thanks #broslow for reminding this stuff).
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
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"