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.

  • I don't use C#, but apparently there must be a way: msdn.microsoft.com/en-us/library/fb4aw7b8(v=vs.110).aspx – Alexander Mills Feb 28 '18 at 7:41
  • 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). – Kusalananda Feb 28 '18 at 7:46
  • I could do this with polling but that would be awful – Alexander Mills Feb 28 '18 at 7:48
  • It would also potentially give you the wrong results. PID reuse may theoretically mean that a process could come alive with the same PID as the process you are waiting for. On Linux (with sequential PIDs) this would be unlikely, but on systems like OpenBSD (randomized PID allocation), it would be an issue. – Kusalananda Feb 28 '18 at 7:52

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


tail --pid=$pid -f /dev/null

Darwin (requires that $pid has open files):

lsof -p $pid +r 1 &>/dev/null

With timeout (seconds)


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)
  • 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. – Stéphane Chazelas Feb 28 '18 at 12:55
  • 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 Feb 28 '18 at 16:18
  • lsof +r puts it in repeat mode, so that command is repeating every second so it's using polling, damn – Alexander Mills Feb 28 '18 at 16:27

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
  • nice that works for me, hopefully doesn't use polling under the hood, thanks! – Alexander Mills Feb 28 '18 at 8:04
  • 2
    yeah won't work for my use case, I get this error: bash: wait: pid 47760 is not a child of this shell...back to the drawing board lol – Alexander Mills Feb 28 '18 at 8:22
  • I have answer, I just posted it thx – Alexander Mills Feb 28 '18 at 8:31
  • 2
    This will only wait for child processes, not for a process unassociated with the current process. – Kusalananda Feb 28 '18 at 8:51

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.


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"
  • 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. – Stéphane Chazelas Dec 29 '20 at 15:38

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.