首页 > 解决方案 > bash: wait for single process from a pipeline

问题描述

I'm starting 2 processes, where one pipes into the other and then background them both. I want to wait until the second process finishes to then decide the faith of the first one.

The following snippet gives the output I'm expecting ("exit 1"). However, wait seems to wait for both processes to be finished (so 5 seconds), which is not what I want.

What have I misunderstood about wait? How can I make it wait for just the second process in the pipeline and decide to wait for the first one (or kill it) later?

#!/bin/sh
{
  sleep 5
  exit 5
} | {
  sleep 1
  exit 1
} &
wait $!
echo "exit $?"

标签: bashshellsubprocesswait

解决方案


You don't have misunderstood the wait builtin behavior. You have misunderstood the shell pipe processing instead.

The wait command behaves as expected, it waits for the end of the subshell from the right part of the pipe (the sleep 1) and it waits for five seconds because this right part doesn't effectively terminate until its pipe input is closed. Its pipe input is closed only when the left part of the pipe closes it on its termination (the sleep 5), thus after five seconds.

To wait for the right part of the pipe, you can use signal to know when the right part is about to finish like that :

#!/bin/dash
{
  sleep 5
  exit 5
} | {
  sleep 1
  kill -s USR1 $$
  exit 1
} &
trap "echo \"Second shell finished\";" USR1
wait $!
echo "exit $?"

This way, you'll get a signal from the right part and the wait command will then only wait until it get these signal (the wait command wait for any signal state change, not only termination). The kill -s USR1 $$ send the USR1 signal to the parent shell. Beware that it can imply termination for the process of the current subshell, you have to know how the process will handle USR1 (i guess sleep ignores it).

However, you can't then get the right exit code from the shell of the right part of the pipe, as it is not effectively terminated until its pipe input is closed. To be more precise, you'll get here the exit status 138 which is the the signal status USR1 (value 10) plus the POSIX signal base (128), not the subshell exit status. You can use different signals (USR1, USR2 (POSIX), non POSIX: .. USR64) to signify an exit status or use the subshell STDOUT or an environment variable to transmit an exit status instead if required.


推荐阅读