Pid file and prevent a duplicate instance from running? - bash

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.

Related

How to get kill command result in bash script

I have a bash script which run a command to get its result and do something depends on the result. Here is the script:
#!/bin/bash
commandResult=$(($myCommand) 2>&1)
if [[ "$commandResult" == *Error* ]]; then
x="failed"
else
x="success"
fi
echo $x
exit 0;
There is no problem with this script, the issue is when I try to kill $myCommand in the middle of running the script via kill -9 $myCommand in command line, the $commandResult will be null and the "success" will be printed.
How could I put the kill result in the $commandResult or any other way to find out if process killed in this script?
Any help would be much appreciated.
You should be checking your command's exit code, not its output to standard error. myCommand should exit with 0 on success, and some non-zero code on failure. If it is killed via the kill command, it's exit code will automatically be 128+n, where n is the signal you used to kill it. Then you can test for success with
if myCommand; then
echo success
exit 0
else
status=$?
echo failure
exit $status
fi
Also, you probably don't need to use kill -9. Start with kill (which sends the gentler TERM signal); if that doesn't work, step up to kill -2 (INT, equivalent of Ctrl-C).

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

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

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.

How can I get both the process id and the exit code from a bash script?

I need a bash script that does the following:
Starts a background process with all output directed to a file
Writes the process's exit code to a file
Returns the process's pid (right away, not when process exits).
The script must exit
I can get the pid but not the exit code:
$ executable >>$log 2>&1 &
pid=`jobs -p`
Or, I can capture the exit code but not the pid:
$ executable >>$log;
# blocked on previous line until process exits
echo $0 >>$log;
How can I do all of these at the same time?
The pid is in $!, no need to run jobs. And the return status is returned by wait:
$executable >> $log 2>&1 &
pid=$!
wait $!
echo $? # return status of $executable
EDIT 1
If I understand the additional requirement as stated in a comment, and you want the script to return immediately (without waiting for the command to finish), then it will not be possible to have the initial script write the exit status of the command. But it is easy enough to have an intermediary write the exit status as soon as the child finishes. Something like:
sh -c "$executable"' & echo pid=$! > pidfile; wait $!; echo $? > exit-status' &
should work.
EDIT 2
As pointed out in the comments, that solution has a race condition: the main script terminates before the pidfile is written. The OP solves this by doing a polling sleep loop, which is an abomination and I fear I will have trouble sleeping at night knowing that I may have motivated such a travesty. IMO, the correct thing to do is to wait until the child is done. Since that is unacceptable, here is a solution that blocks on a read until the pid file exists instead of doing the looping sleep:
{ sh -c "$executable > $log 2>&1 &"'
echo $! > pidfile
echo # Alert parent that the pidfile has been written
wait $!
echo $? > exit-status
' & } | read

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