54

Bash スクリプトでは、次のようなことをしたいと思います。

app1 &
pidApp1=$!
app2 &
pidApp2=$1

timeout 60 wait $pidApp1 $pidApp2
kill -9 $pidApp1 $pidApp2

つまり、バックグラウンドで 2 つのアプリケーションを起動し、それらの作業が完了するまで 60 秒待ちます。次に、その間隔内に終了しない場合は、それらを殺します。

timeout残念ながら、は実行可能ファイルであり、waitはシェル コマンドであるため、上記は機能しません。私はそれを次のように変更しようとしました:

timeout 60 bash -c wait $pidApp1 $pidApp2

waitただし、同じシェル内で起動された PID でのみ呼び出すことができるため、これはまだ機能しません。

何か案は?

4

6 に答える 6

65

あなたの例と受け入れられた答えの両方が非常に複雑です。それがまさにそのユースケースであるため、なぜ使用しないのですか? コマンドには、初期シグナルの送信後にコマンドを終了するための組み込みオプション ( )もあります (デフォルトでは)。timeouttimeout-kSIGKILLSIGTERMman timeout

スクリプトが必ずしもwait待機後に制御フローを再開する必要がない場合、それは単に問題です

timeout -k 60s 60s app1 &
timeout -k 60s 60s app2 &
# [...]

ただし、その場合は、timeout代わりに PID を保存することで簡単です。

pids=()
timeout -k 60s 60s app1 &
pids+=($!)
timeout -k 60s 60s app2 &
pids+=($!)
wait "${pids[@]}"
# [...]

例えば

$ cat t.sh
#!/bin/bash

echo "$(date +%H:%M:%S): start"
pids=()
timeout 10 bash -c 'sleep 5; echo "$(date +%H:%M:%S): job 1 terminated successfully"' &
pids+=($!)
timeout 2 bash -c 'sleep 5; echo "$(date +%H:%M:%S): job 2 terminated successfully"' &
pids+=($!)
wait "${pids[@]}"
echo "$(date +%H:%M:%S): done waiting. both jobs terminated on their own or via timeout; resuming script"

.

$ ./t.sh
08:59:42: start
08:59:47: job 1 terminated successfully
08:59:47: done waiting. both jobs terminated on their own or via timeout; resuming script
于 2014-03-14T07:49:15.070 に答える
25

PID をファイルに書き込み、次のようにアプリを起動します。

pidFile=...
( app ; rm $pidFile ; ) &
pid=$!
echo $pid > $pidFile
( sleep 60 ; if [[ -e $pidFile ]]; then killChildrenOf $pid ; fi ; ) &
killerPid=$!

wait $pid
kill $killerPid

これにより、タイムアウトのためにスリープし、それまでに完了していない場合はプロセスを強制終了する別のプロセスが作成されます。

プロセスがより速く完了すると、PID ファイルが削除され、キラー プロセスが終了します。

killChildrenOfすべてのプロセスをフェッチし、特定の PID のすべての子プロセスを強制終了するスクリプトです。この機能を実装するさまざまな方法については、この質問の回答を参照してください:すべての子プロセスを強制終了する最良の方法

BASH の外に出たい場合は、PID とタイムアウトをディレクトリに書き込んで、そのディレクトリを監視できます。約 1 分ごとにエントリを読み、どのプロセスがまだ残っているか、タイムアウトになっているかどうかを確認します。

編集プロセスが正常に終了したかどうかを知りたい場合は、使用できますkill -0 $pid

EDIT2または、プロセスグループを試すことができます。kevinarpeは次のように述べています: PID(146322) の PGID を取得するには:

ps -fjww -p 146322 | tail -n 1 | awk '{ print $4 }'

私の場合: 145974. 次に、PGID を kill の特別なオプションと共に使用して、グループ内のすべてのプロセスを終了できます。kill -- -145974

于 2012-04-05T12:52:26.930 に答える
1

PIDが終了するまで、またはタイムアウトになるまで待機し、タイムアウトを超えた場合はゼロ以外を返し、終了していないすべてのPIDを出力するbash関数を作成しました。

function wait_timeout {
  local limit=${@:1:1}
  local pids=${@:2}
  local count=0
  while true
  do
    local have_to_wait=false
    for pid in ${pids}; do
      if kill -0 ${pid} &>/dev/null; then
        have_to_wait=true
      else
        pids=`echo ${pids} | sed -e "s/${pid}//g"`
      fi
    done
    if ${have_to_wait} && (( $count < $limit )); then
      count=$(( count + 1 ))
      sleep 1
    else
      echo ${pids}
      return 1
    fi
  done   
  return 0
}

これを使うのはただwait_timeout $timeout $PID1 $PID2 ...

于 2016-12-12T19:21:15.843 に答える