Listen for exit of process given pid $$

19,662

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.

Share:
19,662
Alexander Mills
Author by

Alexander Mills

Updated on September 18, 2022

Comments

  • Alexander Mills
    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
      Kusalananda over 6 years
      In Unix, it's common to wait for child processes using wait in the shell or the wait() 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
      Alexander Mills over 6 years
      I could do this with polling but that would be awful
  • Alexander Mills
    Alexander Mills over 6 years
    nice that works for me, hopefully doesn't use polling under the hood, thanks!
  • Stéphane Chazelas
    Stéphane Chazelas over 6 years
    It'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
    Alexander Mills over 6 years
    I fear that tail/lsof will use polling in this case, at least given the empirical evidence it appears that way on MacOS.
  • Alexander Mills
    Alexander Mills over 6 years
    lsof +r puts it in repeat mode, so that command is repeating every second so it's using polling, damn
  • Stéphane Chazelas
    Stéphane Chazelas over 3 years
    See also until as an equivalent (and slightly more portable as ! is not Bourne) of while !. And kill -s 0 as the POSIX equivalent of POSIX+XSI kill -0.
  • reportaman
    reportaman almost 3 years
    gnu tail is available to install via homebrew on macOS (Darwin), and becomes available as gtail. apple.stackexchange.com/a/69332. Is tail --pid preferable over lsof -p?
  • reportaman
    reportaman almost 3 years
    GNU 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
    reportaman almost 3 years
    GNU 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
    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.