As others have said, there isn't any way to wait on "not" a process ID. However this pattern of code doesn't seem that bad to me, so I offer it as a suggested way of achieving what you're after.
It makes use of Bash arrays which you can then give as a list to the wait
command.
Example
A modified version of your example code, cmd.bash
.
!/bin/bash
array[0]=1
array[0]=2
sleep 30 &
pid=$!
pidArr=()
for i in ${array[@]}; do
for n in $(seq 3); do
if [ $n -eq 1 ]; then
sleep 31 > file &
pidArr+=($!)
else
sleep 32 >> file &
pidArr+=($!)
fi
done
done
echo ${pidArr[@]}
I've substituted sleep
commands in this example just to simulate some long running jobs that we can background. They have nothing to do with this other than to act as stand-ins.
Also the final echo
command is there purely so we can see what happens when the for loops complete. Ultimately we'll replace it with an actual wait
command, but let's not get ahead of ourselves.
Example run #1
So let's run our program and see what happens.
$ ./cmd.bash
24666 24667 24668
Now if we check ps
:
$ ps -eaf|grep sleep
saml 24664 1 0 22:28 pts/9 00:00:00 sleep 30
saml 24666 1 0 22:28 pts/9 00:00:00 sleep 31
saml 24667 1 0 22:28 pts/9 00:00:00 sleep 32
saml 24668 1 0 22:28 pts/9 00:00:00 sleep 32
If we wait a bit, these will ultimately finish. But this shows that if we add the process IDs to an array we can echo
them out afterwards.
Example #2
So let's change that echo
line out and swap in a wait
line now, something like this:
wait ${pidArr[@]}
Now when we run our modified version we see that it's waiting on ALL the process IDs:
$ ./cmd.bash
...after ~30 seconds passes
$
We get back our prompt. So we successfully waited for all the processes. A quick check of ps
:
$ ps -eaf|grep sleep
$
So it worked.
You can use the command wait PID
to wait for a process to end.
You can also retrieve the PID of the last command with $!
In your case, something like this would work:
command1 & #run command1 in background
PID=$! #catch the last PID, here from command1
command2 #run command2 while command1 is running in background
wait $PID #wait for command1, in background, to end
command3 #execute once command1 ended
Following your edit, as you have multiple PIDs and you know them, you can do that:
command1 & #run command1 in background
PID1=xxxxx
PID2=yyyyy
PID3=xxyyy
PID4=yyxxx
command2 #run command2 while command1 is running in background
wait $PID1 $PID2 $PID3 $PID4 #wait for the four processes of command1, in background, to end
command3 #execute once command1 ended
Best Answer
Your
kill
command is backwards.Like many UNIX commands, options that start with a minus must come first, before other arguments.
If you write
it sees the
-INT
as an option, and sendsSIGINT
to0
(0
is a special number meaning all processes in the current process group).But if you write
it sees the
0
, decides there's no more options, so usesSIGTERM
by default. And sends that to the current process group, the same as if you did(it would also try sending
SIGTERM
to-INT
, which would cause a syntax error, but it sendsSIGTERM
to0
first, and never gets that far.)So your main script is getting a
SIGTERM
before it gets to run thewait
andecho DONE
.Add
at the top, just after
and run it again to prove this.
As Stephane Chazelas points out, your backgrounded children (
process1
, etc.) will ignoreSIGINT
by default.In any case, I think sending
SIGTERM
would make more sense.Finally, I'm not sure whether
kill -process group
is guaranteed to go to the children first. Ignoring signals while shutting down might be a good idea.So try this: