Wait for last created process (daemon which forks) to end - bash

I'm writing a wrapper script to use in inittab.
This script starts a daemon and waits for it to terminate.
Here's what I have currently:
#!/bin/bash
/usr/local/bin/mydaemon --lots_of_params_here
while kill -0 `echo $!` 2> /dev/null; do sleep 1; done;
The problem is with the second line; it just returns immediately. If I instead do:
while kill -0 `pgrep mydaemon` 2> /dev/null; do sleep 1; done;
It all works fine, but this isn't a good solution for me as I have other scripts with the prefix mydaemon.
What am I doing wrong?
EDIT:
The problem seems to be related to the daemon fork(). So, I always get the parent pid in $!. I'm looking for ways to solve this problem. Maybe I should use pid files and have mydaemon write its pid there.

You can do the following way to get through your issue.
#!/bin/bash
/usr/local/bin/mydaemon --lots_of_params_here &
wait $!
wait command will wait till the process completes and comes out.
If you are looking for to wait after some other commands then you can store the PID in any other variable and use that.
#!/bin/bash
/usr/local/bin/mydaemon --lots_of_params_here &
mypid=$!
### Some other commands
wait $mypid

Related

Kill a background task in mac

I have a task(appium server) running in the background. I started this task by running the command appium & . After running my tests, i need to kill appium. I tried running kill <pid_of_appium> , but the task is not killed immediately. I manually have to press the Enter Key to kill it.
I initially thought this was a problem with appium alone, but I tried running several background tasks and all of these tasks are getting killed only after pressing the Enter key. How can i handle this through code as I need to stop the background task programmatically using a shell command
Be careful using kill -9. It can cause corrupted data and potential problems associated with that. I found this script that should attempt to kill the process with a signal -15, and then with a signal -9 as a last resort.
#!/bin/bash
# Getting the PID of the process
PID=`pid_of_appium`
# Number of seconds to wait before using "kill -9"
WAIT_SECONDS=10
# Counter to keep count of how many seconds have passed
count=0
while kill $PID > /dev/null
do
# Wait for one second
sleep 1
# Increment the second counter
((count++))
# Has the process been killed? If so, exit the loop.
if ! ps -p $PID > /dev/null ; then
break
fi
# Have we exceeded $WAIT_SECONDS? If so, kill the process with "kill-9"
# and exit the loop
if [ $count -gt $WAIT_SECONDS ]; then
kill -9 $PID
break
fi
done
echo "Process has been killed after $count seconds."
If a task doesn't respond to a general kill command, you can try kill -9 instead. Adding the -9 causes the kill program to dispatch a much more ruthless assassin to carry out the deed than the normal version does.
Give a try to pkill and pgrep:
pgrep, pkill -- find or signal processes by name
To find the process and print the PID you can use:
pgrep -l appium
To kill all the processes you can do:
pkill appium
In case want to send a a KILL 9 signal you could do this;
pkill 9 appium

Checking and killing hanged background processes in a bash script

Say I have this pseudocode in bash
#!/bin/bash
things
for i in {1..3}
do
nohup someScript[i] &
done
wait
for i in {4..6}
do
nohup someScript[i] &
done
wait
otherThings
and say this someScript[i] sometimes end up hanging.
Is there a way I can take the process IDs (with $!)
and check periodically if the process is taking more than a specified amount of time after which I want to kill the hanged processes with kill -9 ?
Unfortunately the answer from #Eugeniu did not work for me, timeout gave an error.
However I found useful doing this routine, I'll post it here so anyone can take advantage of it if in my same problem.
Create another script which goes like this
#!/bin/bash
#monitor.sh
pid=$1
counter=10
while ps -p $pid > /dev/null
do
if [[ $counter -eq 0 ]] ; then
kill -9 $pid
#if it's still there then kill it
fi
counter=$((counter-1))
sleep 1
done
then in the main work you just put
things
for i in {1..3}
do
nohup someScript[i] &
./monitor.sh $! &
done
wait
In this way for any of your someScript you will have a parallel process that checks if it's still there every chosen interval (until maximum time decided by the counter) and that actually quit itself if the associated process dies (or gets killed)
One possible approach:
#!/bin/bash
# things
mypids=()
for i in {1..3}; do
# launch the script with timeout (3600s)
timeout 3600 nohup someScript[i] &
mypids[i]=$! # store the PID
done
wait "${mypids[#]}"

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