Sleep in a While Loop Gets Its Own Pid

Sleep in a while loop gets its own pid

  1. Sleep gets its own PID because it is a process running and just waiting. Try which sleep to see where it is.
  2. You can use ps -uf to see the process tree on your system. From there you can determine what the PPID (parent PID) of the shell (the one running the loop) of the sleep is.

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

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).

Killing a while loop that's running in the background without a PID

I wrote the sentence in a script file which named while.sh, and ran it by shell:

[edemon@CentOS workspace]$ ./while.sh 
[edemon@CentOS workspace]$

There is not PID.
I used top command tool to search my while.sh, it told me:

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND                                                                 
4036 edemon 20 0 5268 756 436 R 97.3 0.0 0:07.93 bash
2469 root 20 0 94412 29m 10m S 7.8 2.0 1:49.19 Xorg
2788 edemon 20 0 74300 12m 10m S 1.9 0.9 1:38.79 nm-applet
4040 edemon 20 0 2708 1072 796 R 1.9 0.1 0:00.01 top

The while's father process is bash, so I killed 4036. The size of alive.log didn't grow any more.

Kill PIDs created in while loop background processes

Right now you're collecting only the last PID, not the PIDs of all your background processes.

If you collect the PIDs, you can wait on them individually -- which lets you check whether the individual jobs failed, or to see which one of them isn't actually finishing:

pids=( )
while read bar; do
foo "$bar" & pids+=( "$!" )
done < bars.txt

for pid in "${pids[@]}"; do
echo "Checking exit status of $pid..."
if wait "$pid"; then
echo "$pid succeeded!"
else
echo "$pid failed!"
fi
done

If you want to track down which specific values processing succeeded or failed for, you can do even better (with bash 4.0 or later):

declare -A pids=( )
while read -r bar; do
foo "$bar" & pids[$!]="$bar"
done < bars.txt

for pid in "${!pids[@]}"; do
bar=${pids[$pid]}
echo "Checking exit status of $pid (processing value $bar)..."
if wait "$pid"; then
echo "$pid (for $bar) succeeded!"
else
echo "$pid (for $bar) failed!"
fi
done

In either of the above cases, each invocation of wait is responsible for returning after the corresponding PID has exited. Thus, when all your background tasks have exited, your script will exit on its own.


Finally, to force your children to exit, you can build a signal handler that takes advantage of the PID list:

shutdown() { kill "${pids[@]}"; } # or "${!pids[@]}" for the second example
trap shutdown 0

Bash kill sleep by parent's PID not working in loop

Bash loops start new process id's as a child. The shell variable $PPID should represent the parent PID. However, I read your prior question as well and I think the better question would be what are you really trying to accomplish? All of this smells hacky...

The statements before wait() aren't being executed, even though the loop isn't infinite (but the children are). Why is that?

I'm not myself an expert, but I have the impression that nothing is being printed because the text is not being flushed to stdout. In some programming languages when you print something, the text is not directly printed but instead it is saved in memory (also known as buffer in some contexts like this one). This is for performance reasons. Sometimes it is more performant to print lots of text to stdout at once than it would be to print small amounts of texts many times.

When you "flush" the text you are removing it from the buffer and printing it to stdout. This is usually done automatically for instance before the process ends or when the buffer is full. If I recall correctly you can also flush the text by printing a line break "\n" because (I am not 100% sure if this is the actual reason, I'm speculating here) in some systems like linux some programs process input line by line and this way the program doesn't have to wait the buffer to be filled or the process finished to start working on the input.

The problem is then that the text is being stored in memory but not printed because it is not being flushed. Try the following:

  1. Printing a newline "\n" after the text
  2. Printing a really long string that wouldn't fit in the buffer.

If I'm right in both cases the text should be displayed in screen. You can also use the fflush() function @pmg suggested in the comment section to explicitly flush the text.

Signalling a bash script running in the background in an infinite loop

This works for me.

EDITED 2014-01-20: This edit avoids the frequent waking up. Also see this question:
Bash: a sleep in a while loop gets its own pid

#!/bin/bash

MY_PID=$$
echo $MY_PID > test.pid

trap 'kill ${!}; run' SIGUSR1

run()
{
echo "signal!"
}

while true
do
sleep 1000 & wait ${!}
done

Running:

> ./test.sh &
[1] 14094
> kill -SIGUSR1 `cat test.pid`
> signal!
>

when is echo after done in a while loop executed

Because the echo " halted ! " is called after done, it is outside of the while loop. This means that "halted!" will only be echoed on the condition that we have broken out of the time-keeping loop, which would happen anytime the script goes back to the "top" of the loop, and checks if test -f /tmp/stop is true. If it is, the time-keeping stops, leading the script to the echo statement, and announcing the time-keeping has stopped (as we aren't in the loop anymore).



Related Topics



Leave a reply



Submit