web-dev-qa-db-ja.com

バックグラウンドプロセスの信頼できる戻りコード

次の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

$?には確かにfooの戻りコードではなくpingの戻りコードが含まれていますか?その質問への答えが「あなたはそれを仮定することはできません」である場合:次に、このコードを変更して、$?には常にfoo?の戻りコードが含まれます

14

bashを使用すると、別のバックグラウンドジョブを開始していない限り、その保証が得られます(バックグラウンドジョブは&だけでなくcoprocおよびプロセス置換でも開始できることに注意してください) )foo &waitの間。

POSIXでは、シェルは少なくとも25個のジョブが終了した後、それらの終了ステータスを記憶する必要があります ですが、bashはそれ以上の情報を記憶しています。

今、あなたがするなら:

foo & pid=$!
...
bar &
wait "$pid"

barfooと同じpidを与えられないという保証はありません(foobarの開始までに終了した場合)。可能性は低いですが、wait "$pid"barの終了ステータスを提供する場合があります。

あなたはそれを再現することができます:

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

(最終的に)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 "$?")
12

はい、信頼できますwait "$!"バックグラウンドジョブのステータスを取得します。スクリプトとして実行する場合、bashは完了したバックグラウンドジョブを自動的に収集しません。したがって、waitを実行すると、waitが呼び出されたときにジョブが収集されます。

これは簡単なスクリプトでテストできます。

#!/bin/bash
sh -c 'sleep 1; exit 22' &
sleep 5
echo "FG: $?"
wait %1
echo "BG: $?"

どちらが出力されます:

FG: 0
BG: 22
4
Patrick

あなたの仮定は正しいと思います。これは、バックグラウンドプロセスの待機に関するman bashからの抜粋です。

Nが存在しないプロセスまたはジョブを指定している場合、戻り状況は127です。それ以外の場合、戻り状況は、最後に待機したプロセスまたはジョブの終了状況です。

したがって、127を確認する必要があります

役立つかもしれないものとはまったく異なる答えを持つ同様の質問があります。

Bashスクリプトはプロセスを待機して戻りコードを取得します

編集1

@Stephaneのコメントと回答に触発されて、私は彼のスクリプトを拡張しました。トラックが緩む前に、約34のバックグラウンドプロセスを開始できます。

tback

$ cat tback 
plist=()
elist=()
slist=([1]=12 [2]=15 [3]=17 [4]=19 [5]=21 [6]=23)
count=30

#start background tasksto monitor
for i in 1 2 3 4
do
  #echo pid $i ${plist[$i]} ${slist[$i]}
  (echo $BASHPID-${slist[$i]} running; exit ${slist[$i]}) & 
  plist[$i]=$!
done

echo starting $count background echos to test history
for i in `eval echo {1..$count}`
do
  echo -n "." &
  elist[$i]=$! 
done
# wait for each background echo to complete
for i in `eval echo {1..$count}`
do
  wait ${elist[$i]}
  echo -n $? 
done
echo ""
# Now wait for each monitored process and check return status with expected
failed=0
for i in 1 2 3 4
do
  wait ${plist[$i]}
  rv=$?
  echo " pid ${plist[$i]} returns $rv should be ${slist[$i]}"
  if [[ $rv != ${slist[$i]} ]] 
  then
    failed=1
  fi
done

wait
echo "Complete $failed"
if [[ $failed = "1" ]]
then
  echo Failed
else
  echo Success
fi
exit $failed
$ 

私のシステムでは

$ bash tback
14553-12 running
14554-15 running
14555-17 running
starting 30 background echos to test history
14556-19 running
..............................000000000000000000000000000000
 pid 14553 returns 12 should be 12
 pid 14554 returns 15 should be 15
 pid 14555 returns 17 should be 17
 pid 14556 returns 19 should be 19
Complete 0
Success
0
X Tian