Reputation: 1001
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?
Upvotes: 3
Views: 2058
Reputation: 1001
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.
Upvotes: 2
Reputation: 47034
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
Upvotes: 0
Reputation: 3206
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
Upvotes: 3