What does `kill -0 $pid` in a shell script do? - bash

Basically, what signal does '0' represent, because here I see SIGNAL numbers starting from 1.

sending the signal 0 to a given PID just checks if any process with the given PID is running and you have the permission to send a signal to it.
For more information see the following manpages:
kill(1)
$ man 1 kill
...
If sig is 0, then no signal is sent, but error checking is still performed.
...
kill(2)
$ man 2 kill
...
If sig is 0, then no signal is sent, but error checking is still performed; this
can be used to check for the existence of a process ID or process group ID.
...

This is a Good Question Because...
...it can be hard to find documentation on this special signal. Despite what others have said, the only mention of this signal in man 1 kill in Debian-based systems is:
Particularly useful signals include HUP, INT, KILL, STOP, CONT, and 0.
Not especially helpful, especially if you don't already know what the signal does. It is also not listed by the output of kill -l, so you won't know about it unless you already know about it.
Where to Find It Documented
On Debian and Ubuntu systems, the output of man 2 kill says, in part:
If sig is 0, then no signal is sent, but error checking is still performed; this can be used to check for the existence of a process ID or process group ID.
What It's Good For
You can use kill -0 to check whether a process is running. Consider these examples.
# Kill the process if it exists and accepts signals from
# the current user.
sleep 60 &
pid=$!
kill -0 $pid && kill $pid
# Check if a PID exists. When missing, this should result
# in output similar to:
# bash: kill: (6228) - No such process
# Exit status: 1
kill -0 $pid; echo "Exit status: $?"
You can also use kill -0 to determine if the current user has permissions to signal a given process. For example:
# See if you have permission to signal the process. If not,
# this should result in output similar to:
# bash: kill: (15764) - Operation not permitted
# Exit status: 1
sudo sleep 60 &
kill -0 $!; echo "Exit status: $?"

kill -0 $pid is to check whether the process with process id (pid) exists or not.
Be careful while using kill -0 $pid to check the process existence because
Once the intended process exit then its pid can be allot to other newly created process. ( So one can not be so sure that particular process is alive or not )
In case of zombie process, for which child is waiting for parent to call wait. Here it hold the $pid and give the positive result while that process is not running.

This command checks wether the process with PID in $pid is alive.

Sending the EXIT signal, or 0 to a process will:
Check for the existence of a process.
Do various error checking on the process (PID, PGID, etc ...).
It will not send any output to stdout upon success.
Send an error message to stderr if something is not right.
Give you a false positive if the process is defunct (i.e. Zombie).
More explicitly, a useful function for your shell scripts would be:
function isProcess ()
{
kill -s EXIT $1 2> /dev/null
}
This returns no text to stdout upon success, but an error message to stderr upon failure (but I have redirected that error message to /dev/null).
If you are concerned about defunct / zombie process status, then you need to use ps, preferably with the --no-headers switch.
#!/bin/ksh
function trim ()
{
echo -n "$1" | tr -d [:space:]
}
function getProcessStatus ()
{
trim $(ps -p $1 -o stat --no-headers)
}
function isZombie ()
{
typeset processStatus=$(getProcessStatus $1)
[[ "$processStatus" == "Z" ]]
return $?
}

kill -0 $pid is used to check if a process running with $pid is alive or not. But this can be tricky, as process ID can be reassigned, once a process exit and new process runs.
One can use killall -0 <process name> to get about a particular process is running or not.

Related

Wait for process to finish, or user input

I have a backgrounded process that I would like to wait for (in case it fails or dies), unless I receive user input. Said another way, the user input should interrupt my waiting.
Here's a simplified snippet of my code
#!/bin/bash
...
mplayer -noconsolecontrols "$media_url" &
sleep 10 # enough time for it to fail
ps -p $!
if [ $? -ne 0 ]
then
fallback
else
read
kill $!
fi
The line that I particularly dislike is sleep 10, which is bad because it could be too much time, or not enough time.
Is there a way to wait $! || read or the equivalent?
Use kill -0 to validate that the process is still there and read with a timeout of 0 to test for user input. Something like this?
pid=$!
while kill -0 $pid; do
read -t 0 && exit
sleep 1
done
Original
ps -p to check the process. read -t 1 to wait for user input.
pid=$!
got_input=142
while ps -p $pid > /dev/null; do
if read -t 1; then
got_input=$?
kill $pid
fi
done
This allows for branching based whether the process died, or was killed due to user input.
All credit to gubblebozer. The only reason I'm posting this answer is the claim by moderators that my edits to his post constituted altering his intent.
Anti Race-Condition
First off, a race condition involving pids is (very likely) not a concern if you're fairly quick, because they're reused on a cycle.
Even so, I guess anything is possible... Here's some code that handles that possibility, without breaking your head on traps.
got_input=142
while true; do
if read -t 1; then
got_input=$?
pkill --ns $$ name > /dev/null
break
elif ! pgrep --ns $$ name > /dev/null; then
break
fi
done
Now, we've accomplished our goal, while (probably) completely eliminating the race condition.
Any loop with a sleep or similar timeout in it, will introduce a race condition. It's better to actively wait for the process to die, or, in this case, to trap the signal that's sent when a child dies.
#!/bin/bash
set -o monitor
trap stop_process SIGCHLD
stop_process()
{
echo sigchld received
exit
}
# the background process: (this simulates a process that exits after 10 seconds)
sleep 10 &
procpid=$!
echo pid of process: $procpid
echo -n hit enter:
read
# not reached when SIGCHLD is received
echo killing pid $procpid
kill $procpid
I'm not 100% sure this eliminates any race condition, but it's a lot closer than a sleep loop.
edit: the shorter, less verbose version
#!/bin/bash
set -o monitor
trap exit SIGCHLD
sleep 5 &
read -p "hit enter: "
kill $!
edit 2: setting the trap before starting the background process prevents another race condition in which the process would die before the trap was installed

Pid file and prevent a duplicate instance from running?

I found the following lines at the top of one of our scripts. I understand that it verifies if the PID file is already running it will kill the script to run twice, but I don't really understand what each statement does?
For example, if kill -0 &>1 > /dev/null $pid; then. Can someone help me understanding the logic behind this?
pidfile=/tmp/backup_meb.pid
if [ -e $pidfile ]; then
pid=`cat $pidfile`
if kill -0 &>1 > /dev/null $pid; then
echo "Already running"
exit 1
else
rm $pidfile
fi
fi
echo $$ > $pidfile
kill man page:
If sig is 0, then no signal is sent, but error checking is still performed.
if operates on the return code of the list it executes. In this case the return status of kill.
if pidfile exists then
set pid to the contents of pidfile
if pid is a valid process ID
print "Already running" and exit
otherwise
delete pidfile
create a new pidfile that contains the ID of the current process
The kill -0 command sends a null signal to the process and the exit code will tell you if the process exists/can be accessed. The &>1 redirects stdout to /dev/null. $$ is an internal variable in bash that contains the process ID of the script (or shell) itself.

Using while or until to wait until a PID doesn't exist

I have been using Bash to wait until a PID no longer exists. I've tried
#!/bin/bash
while [ kill -0 PID > /dev/null 2>&1 ]; do
//code to kill process
done
//code to execute after process is dead
as well as
#!/bin/bash
until [ ! kill -0 PID > /dev/null 2>&1 ]; do
//code to kill process
done
//code to execute after process is dead
Both these examples either fail to work, or keep on looping after the process has ended. What am I doing incorrectly?
You should be simply doing:
while kill -0 $PID >/dev/null 2>&1
do
# Code to kill process
done
The loop condition tests the exit status of the last command — in this case, kill. The -0 option (used in the question) doesn't actually send any signal to the process, but it does check whether a signal could be sent — and it can't be sent if the process no longer exists.
(See the POSIX specification of the kill() function and the POSIX kill utility.)
The significance of 'last' is that you could write:
while sleep 1
echo Testing again
kill -0 $PID >/dev/null 2>&1
do
# Code to kill process
done
This too tests the exit status of kill (and kill alone).
Also you can do in unixes with procfs (almost all except mac os)
while test -d /proc/$PID; do
kill -$SIGNAL $PID
# optionally
sleep 0.2
done

Check if PID exists in Bash

I want to stall the execution of my BASH script until a process is closed (I have the PID stored in a variable). I'm thinking
while [PID IS RUNNING]; do
sleep 500
done
Most of the examples I have seen use /dev/null which seems to require root. Is there a way to do this without requiring root?
Thank you very much in advance!
kill -s 0 $pid will return success if $pid is running, failure otherwise, without actually sending a signal to the process, so you can use that in your if statement directly.
wait $pid will wait on that process, replacing your whole loop.
It seems like you want
wait $pid
which will return when $pid finishes.
Otherwise you can use
ps -p $pid
to check if the process is still alive (this is more effective than kill -0 $pid because it will work even if you don't own the pid).
You might look for the presence of /proc/YOUR_PID directory.
ps --pid $pid &>/dev/null
returns 0 if it exists, 1 otherwise
I always use the following
tail -f /dev/null --pid $PID. It doesn't require explicit loop and isn't limited to your shell's children pids only.

How to suppress Terminated message after killing in bash?

How can you suppress the Terminated message that comes up after you kill a
process in a bash script?
I tried set +bm, but that doesn't work.
I know another solution involves calling exec 2> /dev/null, but is that
reliable? How do I reset it back so that I can continue to see stderr?
In order to silence the message, you must be redirecting stderr at the time the message is generated. Because the kill command sends a signal and doesn't wait for the target process to respond, redirecting stderr of the kill command does you no good. The bash builtin wait was made specifically for this purpose.
Here is very simple example that kills the most recent background command. (Learn more about $! here.)
kill $!
wait $! 2>/dev/null
Because both kill and wait accept multiple pids, you can also do batch kills. Here is an example that kills all background processes (of the current process/script of course).
kill $(jobs -rp)
wait $(jobs -rp) 2>/dev/null
I was led here from bash: silently kill background function process.
The short answer is that you can't. Bash always prints the status of foreground jobs. The monitoring flag only applies for background jobs, and only for interactive shells, not scripts.
see notify_of_job_status() in jobs.c.
As you say, you can redirect so standard error is pointing to /dev/null but then you miss any other error messages. You can make it temporary by doing the redirection in a subshell which runs the script. This leaves the original environment alone.
(script 2> /dev/null)
which will lose all error messages, but just from that script, not from anything else run in that shell.
You can save and restore standard error, by redirecting a new filedescriptor to point there:
exec 3>&2 # 3 is now a copy of 2
exec 2> /dev/null # 2 now points to /dev/null
script # run script with redirected stderr
exec 2>&3 # restore stderr to saved
exec 3>&- # close saved version
But I wouldn't recommend this -- the only upside from the first one is that it saves a sub-shell invocation, while being more complicated and, possibly even altering the behavior of the script, if the script alters file descriptors.
EDIT:
For more appropriate answer check answer given by Mark Edgar
Solution: use SIGINT (works only in non-interactive shells)
Demo:
cat > silent.sh <<"EOF"
sleep 100 &
kill -INT $!
sleep 1
EOF
sh silent.sh
http://thread.gmane.org/gmane.comp.shells.bash.bugs/15798
Maybe detach the process from the current shell process by calling disown?
The Terminated is logged by the default signal handler of bash 3.x and 4.x. Just trap the TERM signal at the very first of child process:
#!/bin/sh
## assume script name is test.sh
foo() {
trap 'exit 0' TERM ## here is the key
while true; do sleep 1; done
}
echo before child
ps aux | grep 'test\.s[h]\|slee[p]'
foo &
pid=$!
sleep 1 # wait trap is done
echo before kill
ps aux | grep 'test\.s[h]\|slee[p]'
kill $pid ## no need to redirect stdin/stderr
sleep 1 # wait kill is done
echo after kill
ps aux | grep 'test\.s[h]\|slee[p]'
Is this what we are all looking for?
Not wanted:
$ sleep 3 &
[1] 234
<pressing enter a few times....>
$
$
[1]+ Done sleep 3
$
Wanted:
$ (set +m; sleep 3 &)
<again, pressing enter several times....>
$
$
$
$
$
As you can see, no job end message. Works for me in bash scripts as well, also for killed background processes.
'set +m' disables job control (see 'help set') for the current shell. So if you enter your command in a subshell (as done here in brackets) you will not influence the job control settings of the current shell. Only disadvantage is that you need to get the pid of your background process back to the current shell if you want to check whether it has terminated, or evaluate the return code.
This also works for killall (for those who prefer it):
killall -s SIGINT (yourprogram)
suppresses the message... I was running mpg123 in background mode.
It could only silently be killed by sending a ctrl-c (SIGINT) instead of a SIGTERM (default).
disown did exactly the right thing for me -- the exec 3>&2 is risky for a lot of reasons -- set +bm didn't seem to work inside a script, only at the command prompt
Had success with adding 'jobs 2>&1 >/dev/null' to the script, not certain if it will help anyone else's script, but here is a sample.
while true; do echo $RANDOM; done | while read line
do
echo Random is $line the last jobid is $(jobs -lp)
jobs 2>&1 >/dev/null
sleep 3
done
Another way to disable job notifications is to place your command to be backgrounded in a sh -c 'cmd &' construct.
#!/bin/bash
# ...
pid="`sh -c 'sleep 30 & echo ${!}' | head -1`"
kill "$pid"
# ...
# or put several cmds in sh -c '...' construct
sh -c '
sleep 30 &
pid="${!}"
sleep 5
kill "${pid}"
'
I found that putting the kill command in a function and then backgrounding the function suppresses the termination output
function killCmd() {
kill $1
}
killCmd $somePID &
Simple:
{ kill $! } 2>/dev/null
Advantage? can use any signal
ex:
{ kill -9 $PID } 2>/dev/null

Resources