1

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 ?

Atharva
  • 6,711
  • 5
  • 31
  • 39
  • 1
    Are you sure `echo` isn't happening? What happens if you direct its output to a temporary file, like `echo "Done sending signal" >> /tmp/whatever` and see if `/tmp/whatever` has something in it afterwards. It's possible that you're running this in a subshell, maybe? Or some other environment that traps stdout? – imm Jul 07 '12 at 14:35
  • Yes, I am sure. I am also guessing the error must be occurring in the execution of kill command but it's not showing up in the terminal. So If you someone can suggest me a way to know what value the kill command returns or whether some error occurred while its execution ? – Atharva Jul 07 '12 at 14:40
  • See also http://stackoverflow.com/questions/696839/how-do-i-write-a-bash-script-to-restart-a-process-if-it-dies – tripleee Nov 12 '15 at 04:50
  • All of the answers here fail to point out that `grep | awk` is an [antipattern](http://www.iki.fi/era/unix/award.html#grep) -- you want `awk "/$1/ && !/awk/ { print \$1 }'` to replace `grep | grep | awk` to fix the bug *and* the antipattern. – tripleee Nov 12 '15 at 04:52

3 Answers3

1

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.

Charlie Martin
  • 110,348
  • 25
  • 193
  • 263
  • So change the echo in, "Process with PID=$$ is done sending signal": is that PID among the victims? – LSerni Jul 07 '12 at 14:44
  • Okay I am noticing the strange thing. The script is working but not as expected. `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 ? – Atharva Jul 07 '12 at 14:59
0

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."
LSerni
  • 55,617
  • 10
  • 65
  • 107
  • I followed your suggestion, please check my updated question. – Atharva Jul 07 '12 at 15:08
  • $$ instead of ^$$\$ did a better job. What does ^$$\$ mean. From my knowledge of regex it seems to for starting with $$ but can't understand \$ ? – Atharva Jul 07 '12 at 16:11
  • 1
    That was because I thought, if you happen to want to kill process 17335 and you do this from a process with pid 733, 17335 will be matched by 733 and graced undesiredly. So I specified ^ ("at the start") and $ ("at the end"). And since $ is a shell metacharacted, I had to escape it. So "^$$\$": "a line containing only $$". – LSerni Jul 08 '12 at 21:48
  • Downvote: While this fixes the problem, the use of an unnecessary array and the hideous string of `grep` processes is not a good example of how to code this. – tripleee Nov 12 '15 at 04:54
  • Sorry, @tripleee - what array? The OP was basically reimplementing the `killall` utility and his script worked on my machine. Not knowing his `ps` version (and hence output format), I had to improvise. But it's my understanding that *hideous lines of piped processes* is how Unix tends to do things. – LSerni Nov 12 '15 at 18:27
0

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"
carlo
  • 1