バックグラウンドプロセスを監視し、必要に応じて終了しながら終了コードを取得する信頼性の高い方法

バックグラウンドプロセスを監視し、必要に応じて終了しながら終了コードを取得する信頼性の高い方法

私は、それが実現できると思われる設定を思いつきましたが、うまくいきませんでした。

#!/bin/bash

echo "Launching a background process that may take hours to finish.."
myprog &
pid=$!
retval=
##At this time pid should hold the process id of myprog
echo "pid=${pid}"

{
    ##check if the process is still running
    psl=$(ps -f -p ${pid} | grep -E "\bmyprog\b")
    killit=
    while [[ ! -z ${psl} ]]
    do
        ##if a file named "kill_flag" is detected, kill the process
        if [[ -e "kill_flag" ]]
        then
            killit=YES
            break
        fi
        #check every 3 seconds
        sleep 3
        psl=$(ps -f -p ${pid} | grep -E "\bmyprog\b")
    done

    ##killit not set, normal exit, read from fd5
    if [[ -z ${killit} ]]
    then
        read <&5 retval
  else
    ##kill here, the wait will return and the sub process ends
    kill ${pid}
  fi

} 5< <( wait ${pid} > /dev/null 2>&1; echo $? )

echo "retval=$retval"

最初の実行では、すべて正常に見えます。 でプロセスを強制終了できますtouch kill_flag。それ以外の場合は、myprog が正常に終了するまで待機します。しかし、その後、retval で常に -1 が返されることに気付きました。myprog は、通常の実行で確認されたように 0 を返します。さらに調査すると、" echo $?" の部分は、wait コマンドが終了した後ではなく、スクリプトの起動直後に実行されたことがわかりました。ここで何が起こっているのか疑問に思っています。私は bash の初心者です。

答え1

wait現在のシェル プロセスの子プロセスに対してのみ機能します。内部のコードを解釈するサブシェルは、<(...)姉妹プロセスを待つことはできません。

待機は、PID を開始した同じシェル プロセスによって実行する必要があります。代わりにzshbash(ここでは他のバックグラウンド ジョブが実行されていないと想定しています) を使用します。

cmd & pid=$!
while (($#jobstates)) {
  [[ -e killfile ]] && kill $pid
  sleep 3
}
wait $pid; echo $?

答え2

実行可能なバージョンを見つけました:

#!/bin/bash
export retval=
##At this time pid should hold the process id of myprog
{
    ##This is the subshell that launched and monitoring myprog
    subsh=$!

    ##Since myprog is probably the only child process of this subsh, it should be pretty safe
    pid=$(ps -f --ppid ${subsh} | grep -E "\bmyprog\b" | gawk '{print $2}' )
    ##check if the process is still running
    psl=$(ps -f -p ${pid} | grep -E "\bmyprog\b")
    killit=
    while [[ ! -z ${psl} ]]
    do
        ##if a file named "kill_flag" is detected, kill the process
        if [[ -e "kill_flag" ]]
        then
            killit=YES
            break
        fi
        #check every 3 seconds
        sleep 3
        psl=$(ps -f -p ${pid} | grep -E "\bmyprog\b")
    done

    ##killit not set, normal exit, read from fd5
    if [[ -z ${killit} ]]
    then
        read <&5 retval
  else
    ##kill here, the wait will return and the sub process ends
    kill ${pid}
  fi

} 5< <( myprog >>logfile 2>&1; echo $? )

echo "retval=$retval"

唯一の厄介な点は、セマフォを使用して myprog を強制終了すると、プロセス置換が終了しているためエラーが発生しますが、これは簡単にトラップできることです。

関連情報