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.
Best Answer
Without
-t
,sshd
gets the stdout of the remote shell (and children likesleep
) and stderr via two pipes (and also sends the client's input via another pipe).sshd
does wait for the process in which it has started the user's login shell, but also, after that process has terminated waits for eof on the stdout pipe (not the stderr pipe in the case of openssh at least).And eof happens when there's no file descriptor by any process open on the writing end of the pipe, which typically only happens when all the processes that didn't have their stdout redirected to something else are gone.
When you use
-t
,sshd
doesn't use pipes. Instead, all the interaction (stdin, stdout, stderr) with the remote shell and its children are done using one pseudo-terminal pair.With a pseudo-terminal pair, for
sshd
interacting with the master side, there's no similar eof handling and while at least some systems provide alternative ways to know if there are still processes with fds open to the slave side of the pseudo-terminal (see @JdeBP comment below),sshd
doesn't use them, so it just waits for the termination of the process in which it executed the login shell of the remote user and then exits.Upon that exit, the master side of the pty pair is closed
which means the pty is destroyed, so processes controlled by the slave will receive a SIGHUP (which by default would terminate them).Edit: that last part was incorrect, though the end result is the same. See @pynexj's answer for a correct description of what exactly happens.