后台进程的可靠返回码

Chr*_*idt 16 bash shell-script exit background-process

让我们假设以下 bash 代码:

foo > logfile 2>&1 &
foo_pid=$!

while ps -p$foo_pid
do
    ping -c 1 localhost
done

wait $foo_pid

if [[ $? == 0 ]]
then
    echo "foo success"
fi
Run Code Online (Sandbox Code Playgroud)

假设$?确实包含 的返回码foo而不是 的返回码是否安全ping?如果这个问题的答案是:“你不能假设。” 那么我如何修改这段代码以确保$?始终包含的返回码foo

Sté*_*las 15

有了bash,你将有保证,除非你已经开始另一个后台作业(和提防后台作业可以开始&,但也与coproc对之间,并与进程替换)foo &wait

POSIX 要求 shell 在它们消失后bash记住至少 25 个作业的退出状态,但记住的远不止这些。

现在,如果你这样做:

foo & pid=$!
...
bar &
wait "$pid"
Run Code Online (Sandbox Code Playgroud)

您无法保证bar不会获得与foo(如果foobar开始时已终止)相同的 pid ,因此即使不太可能,wait "$pid"也可能会给您bar.

您可以使用以下方法重现它:

bash -c '(exit 12; foo) & pid=$!
         while : bar & [ "$pid" != "$!" ]; do :;done
         wait "$pid"; echo "$?"'
Run Code Online (Sandbox Code Playgroud)

这将(最终)给你0而不是12.

为了避免这个问题,一种方法是将其写为:

{
  foo_pid=$!

  while ps -p "$foo_pid"
  do
      ping -c 1 localhost
  done

  bar &
  ...

  read <&3 ret
  if [ "$ret" = 0 ]; then
    echo foo was sucessful.
  fi
} 3< <(foo > logfile 2>&1; echo "$?")
Run Code Online (Sandbox Code Playgroud)