Listen for exit of process given pid $$
Solution 1
I got what I needed from this answer: https://stackoverflow.com/a/41613532/1223975
..turns out using wait <pid>
will only work if that pid is a child process of the current process.
However the following will work for any process:
To wait for any process to finish
Linux:
tail --pid=$pid -f /dev/null
Darwin (requires that $pid
has open files):
lsof -p $pid +r 1 &>/dev/null
With timeout (seconds)
Linux:
timeout $timeout tail --pid=$pid -f /dev/null
Darwin (requires that $pid
has open files):
lsof -p $pid +r 1m%s -t | grep -qm1 $(date -v+${timeout}S +%s 2>/dev/null || echo INF)
Solution 2
You can use the bash builtin wait
:
$ sleep 10 &
[2] 28751
$ wait 28751
[2]- Done sleep 10
$ help wait
wait: wait [-n] [id ...]
Wait for job completion and return exit status.
Waits for each process identified by an ID, which may be a process ID or a
job specification, and reports its termination status. If ID is not
given, waits for all currently active child processes, and the return
status is zero. If ID is a a job specification, waits for all processes
in that job's pipeline.
If the -n option is supplied, waits for the next job to terminate and
returns its exit status.
Exit Status:
Returns the status of the last ID; fails if ID is invalid or an invalid
option is given.
It uses the system call waitpid()
..
$ whatis waitpid
waitpid (2) - wait for process to change state
Solution 3
The portable way is to poll using kill, i.e. something like:
while ! kill -0 "$pid" 2>/dev/null; do sleep 1; done
This doesn't require non-portable commands such as GNU tail or lsof, and, in bash, only invokes one external command, namely sleep. GNU tail is likely a bit more efficient, since it was written in C (and could, in theory, take advantage of advanced functions such as pidfd_open
), and lsof is likely going to have a lot of overhead.
A rough timeout can be implemented by counting the number of loop iterations.
A slightly less portable solution that only works for one waiter is to use ptrace(2), which doesn't have a portable shell interface, but which can be accessed using the strace command on some systems:
strace -e exit -e signal=none -p "$pid"
Solution 4
Regarding the https://stackoverflow.com/a/41613532/1223975 solution that Alexander Mills reposted, Timeout in Seconds
Darwin
, is a workaround for a UNIX-like OS that does not have GNU tail
. It is not specific to Darwin
, but, depending on the age of the UNIX-like operating system, the command-line offered is more complex than necessary, and can fail:
lsof -p $pid +r 1m%s -t | grep -qm1 $(date -v+${timeout}S +%s 2>/dev/null || echo INF)
On at least one old UNIX, the lsof
argument +r 1m%s
fails (even for a superuser):
lsof: can't read kernel name list.
The m%s
is an output format specification. A simpler post-processor does not require it. For example, the following command waits on PID 5959 for up to five seconds:
lsof -p 5959 +r 1 | awk '/^=/ { if (T++ >= 5) { exit 1 } }'
In this example, if PID 5959 exits of its own accord before the five seconds elapses, ${?}
is 0
. If not ${?}
returns 1
after five seconds.
It may also be worth expressly noting that in +r 1
, the 1
is the poll interval (in seconds), so it may be changed to suit the situation.
Alexander Mills
Updated on September 18, 2022Comments
-
Alexander Mills over 1 year
Say I have a pid in hand,
mypid=$$
is there some bash/system command I can use to listen for the exit of that process with the given pid?
If no process with mypid exists, I guess the command should simply fail.
-
Kusalananda over 6 yearsIn Unix, it's common to wait for child processes using
wait
in the shell or thewait()
C library function. There is AFAIK no standard way of waiting for a non-child process. It is further unclear if the C# function can do that (it depends on what an "associated process" is). -
Alexander Mills over 6 yearsI could do this with polling but that would be awful
-
-
Alexander Mills over 6 yearsnice that works for me, hopefully doesn't use polling under the hood, thanks!
-
Stéphane Chazelas over 6 yearsIt's not related to Linux, it's a feature of GNU tail. So it will work on any system with GNU tail like GNU/Linux ones, and it wouldn't work on non-GNU Linux-based systems.
-
Alexander Mills over 6 yearsI fear that tail/lsof will use polling in this case, at least given the empirical evidence it appears that way on MacOS.
-
Alexander Mills over 6 yearslsof +r puts it in repeat mode, so that command is repeating every second so it's using polling, damn
-
Stéphane Chazelas over 3 yearsSee also
until
as an equivalent (and slightly more portable as!
is not Bourne) ofwhile !
. Andkill -s 0
as the POSIX equivalent of POSIX+XSIkill -0
. -
reportaman almost 3 yearsgnu
tail
is available to install via homebrew on macOS (Darwin), and becomes available asgtail
. apple.stackexchange.com/a/69332. Istail --pid
preferable overlsof -p
? -
reportaman almost 3 yearsGNU tail is available as
gtail
after you install coreutils via homebrew (works even on M1 Mac): apple.stackexchange.com/a/69332. The answer unix.stackexchange.com/a/626632/456507 suggests that GNU tail will be more efficient compared to lsof for the job, though idk. -
reportaman almost 3 yearsGNU tail is available as
gtail
after you install coreutils via homebrew (works even on M1 Mac): apple.stackexchange.com/a/69332. Are we sure if tail is much more efficient for the job compared to lsof? am not a unix/linux expert. -
ZeeLoony almost 3 years"a UNIX-like OS that does not have GNU tail" is not necessarily an Apple OS that has ready access to GNU tail built for it. This answer was written expressly for a situation where GNU tail was not available or a user didn't want to have to get it. A careful read of the answer shows that the goal of the answer was to supply a solution that had greater cross-platform compatibility.