プロセスが完了するのを待ちます


147

Bashには、プロセスの終了を待つ組み込みの機能はありますか?

このwaitコマンドでは、子プロセスが終了するのを待つだけです。スクリプトを続行する前に、プロセスが完了するのを待つ方法があるかどうか知りたいのですが。

これを行うための機械的な方法は次のとおりですが、Bashに組み込みの機能があるかどうかを知りたいです。

while ps -p `cat $PID_FILE` > /dev/null; do sleep 1; done

4
2つの注意点を挙げましょう。1. mp3foleyによって以下で指摘されているように、「kill -0」はPOSIXで常に機能するとは限りません。2.おそらく、プロセスがゾンビではないことを確認する必要もあります。ゾンビは、実質的に終了したプロセスです。詳細については、mp3foleyのコメント私の記事を参照してください。
かいらテイカ

2
もう1つの注意もともとは以下のks1322で指摘されます):子プロセス以外のPIDの使用は堅牢ではありません。安全な方法が必要な場合は、IPCなどを使用してください。
かいらていか

回答:


138

プロセスが完了するのを待つ

Linux:

tail --pid=$pid -f /dev/null

ダーウィン($pid開いているファイルが必要です):

lsof -p $pid +r 1 &>/dev/null

タイムアウトあり(秒)

Linux:

timeout $timeout tail --pid=$pid -f /dev/null

ダーウィン($pid開いているファイルが必要です):

lsof -p $pid +r 1m%s -t | grep -qm1 $(date -v+${timeout}S +%s 2>/dev/null || echo INF)

42
だれがそれをtailするか知っていただろう。
ctrl-alt-delor 2017

8
tailkill(pid, SIG_0)プロセスをポーリングして内部で動作します(を使用して検出されますstrace)。
Righは、2017年

2
lsofはポーリング、つまり+r 1タイムアウトを使用していることに注意してください。個人的には、ポーリングを使用しないMacOSのソリューションを探しています。
Alexander Mills

1
このトリックゾンビでは失敗します。強制終了できないプロセスには問題ありません。tailラインを持っていますkill (pid, 0) != 0 && errno != EPERM
かいらテイカ

2
@AlexanderMillsは、macOSシステムがコマンドの実行時にスリープ状態にならないことを許容できる場合caffeinate -w $pidは、トリックを実行します。
zneak 2018

83

ビルトインはありません。kill -0実行可能なソリューションのループで使用します。

anywait(){

    for pid in "$@"; do
        while kill -0 "$pid"; do
            sleep 0.5
        done
    done
}

または、簡単な1回限りの使用のためのよりシンプルなonelinerとして:

while kill -0 PIDS 2> /dev/null; do sleep 1; done;

いくつかのコメンテーターが指摘しているように、シグナルを送信する特権を持っていないプロセスを待機したい場合は、kill -0 $pid呼び出しを置き換えるためにプロセスが実行されているかどうかを検出する別の方法があります。Linuxではtest -d "/proc/$pid"動作しますが、他のシステムではpgrep(使用可能な場合)またはのようなものを使用する必要がある場合がありますps | grep "^$pid "


2
注意:これは、常に仕事をしません下の指摘によってmp3foley。詳細については、そのコメントと私の記事を参照してください。
かいらていか

2
注意2(ゾンビについて):上記のテディによるフォローアップコメントは、ゾンビの可能性があるため、まだ十分ではありません。Linuxソリューションについては、以下の私の回答を参照しください。
かいらていか2014

4
この解決策は競合状態を危険にさらしませんか?でスリープしている間にsleep 0.5、withでプロセス$pidが停止し、同じプロセスで別のプロセスが作成されることがあります$pid。そして、同じ2つの異なるプロセス(またはそれ以上)を待つことになり$pidます。
ks1322 2014

2
@ ks1322はい、このコードには実際に競合状態があります。
テディ

4
PIDは通常、順次生成されませんか?カウントが1秒でラップする可能性はどれくらいですか?
esmiralha 2016

53

プロセスがルート(またはその他)によって所有されている場合、「kill -0」は機能しないので、pgrepを使用して次のように思い付きました。

while pgrep -u root process_name > /dev/null; do sleep 1; done

これには、おそらくゾンビプロセスと一致するという欠点があります。


2
良い観察。POSIXでは、kill(pid, sig=0)呼び出し元のプロセスにkillする特権がない場合、システムコールは失敗します。したがって、/ bin / kill -0および "kill -0"(bashビルトイン)も同じ条件で失敗します。
かいらテイカ2013

31

このbashスクリプトのループは、プロセスが存在しないか、ゾンビの場合に終了します。

PID=<pid to watch>
while s=`ps -p $PID -o s=` && [[ "$s" && "$s" != 'Z' ]]; do
    sleep 1
done

編集:上記のスクリプトRockalliteによって以下に提供されました。ありがとう!

依存するLinux用の作品以下の私のorignalの答え、procfsすなわち/proc/。私はその移植性を知りません:

while [[ ( -d /proc/$PID ) && ( -z `grep zombie /proc/$PID/status` ) ]]; do
    sleep 1
done

これはシェルに限定されていませんが、OS自体には、非子プロセスの終了を監視するシステムコールがありません。


1
良いですね。私はgrep /proc/$PID/status二重引用符(bash: test: argument expected)で囲む必要がありましたが
Griddo

ハム...もう一度試したところ、うまくいきました。私は前回何か間違ったことをしたと思います。
Griddo 2014年

7
またはwhile s=`ps -p $PID -o s=` && [[ "$s" && "$s" != 'Z' ]]; do sleep 1; done
Rockallite

1
残念ながら、これはBusyBoxでは機能しません-その中でps、どちら-pでもないかs=、サポートされていません
ZimbiX

14

FreeBSDとSolarisには、この便利なpwait(1)ユーティリティがあります。これは、ユーザーの希望どおりに機能します。

他の最近のOSにも必要なシステムコールがあります(たとえば、MacOSはBSDを実装kqueueしています)が、すべてがコマンドラインから使用できるわけではありません。


2
> BSD and Solaris:頭に浮かぶ3つの大きなBSDの検査。Man.openbsd.orgで簡単に確認できるように、OpenBSDもNetBSDも(manページに)この機能はありません。FreeBSDだけにあります。
benaryorg 2017年

あなたは正しいようです。Mea culpa ...それらはすべてを実装kqueueしているので、FreeBSDのコンパイルpwait(1)は簡単です。他のBSDの機能がインポートされないのはなぜ私が逃げるのか...
ミハイルT.

1
plink me@oracle box -pw redacted "pwait 6998";email -b -s "It's done" etc今から数時間ではなく今すぐ家に帰ることを許可しました。
zzxyz 2017

11

bashのマンページから

   wait [n ...]
          Wait for each specified process and return its termination  status
          Each  n  may be a process ID or a job specification; if a
          job spec is given, all processes  in  that  job's  pipeline  are
          waited  for.  If n is not given, all currently active child processes
          are waited for, and the return  status  is  zero.   If  n
          specifies  a  non-existent  process or job, the return status is
          127.  Otherwise, the return status is the  exit  status  of  the
          last process or job waited for.

56
それは事実ですが、現在のシェルの子を待つことしかできません。あなたは待つことができない任意のプロセス。
gumik 2012

@gumik:"nが指定されていない場合、現在アクティブなすべての子プロセスが待機されます"。これは完全に機能します。wait引数がないと、子プロセスが完了するまでプロセスがブロックされます。正直なところ、常にシステムプロセスが進行しているためプロセスを待つ必要はありません。
coderofsalvation 16

1
@coderofsalvation(sleep 10&sleep 3&wait)が戻るまでに10秒かかります。すべての子プロセスが完了するまで、引数なしの待機はブロックされます。OPは、最初の子(または指名された)プロセスが終了したときに通知されることを望んでいます。
android.weasel

また、プロセスがバックグラウンドでもフォアグラウンドでもない場合(Solaris、Linux、またはCygwin)は機能しません。例 sleep 1000 ctrl-z wait [sleep pid]すぐに戻る
zzxyz 2017

6

これらのソリューションはすべてUbuntu 14.04でテストされています。

解決策1(psコマンドを使用): Pierzの回答を合計するために、私は次のことをお勧めします。

while ps axg | grep -vw grep | grep -w process_name > /dev/null; do sleep 1; done

この場合、grep -vw grepgrepがprocess_nameのみと一致し、grep自体とは一致しないことを確認します。これは、process_nameがの行末にない場合をサポートするという利点がありps axgます。

解決策2(topコマンドとプロセス名を使用):

while [[ $(awk '$12=="process_name" {print $0}' <(top -n 1 -b)) ]]; do sleep 1; done

process_name表示されるプロセス名に置き換えtop -n 1 -bます。引用符を保持してください。

プロセスが完了するのを待つプロセスのリストを表示するには、次のコマンドを実行できます。

while : ; do p=$(awk '$12=="process_name" {print $0}' <(top -n 1 -b)); [[ $b ]] || break; echo $p; sleep 1; done

解決策3(topコマンドとプロセスIDを使用):

while [[ $(awk '$1=="process_id" {print $0}' <(top -n 1 -b)) ]]; do sleep 1; done

process_idプログラムのプロセスIDに置き換えます。


4
反対票:長いgrep -v grepパイプラインは大規模なアンチパターンであり、これは同じ名前の無関係なプロセスがないことを前提としています。代わりにPIDがわかっている場合、これは適切に機能するソリューションに適応できます。
Tripleee 2016

コメントをくれたtripleeeに感謝します。-w問題をgrep -v grep ある程度回避するためにフラグを追加しました。また、あなたのコメントに基づいて2つの解決策を追加しました。
Saeid BK 2016

5

さて、答えはそうです-いいえ、組み込みのツールはありません。

に設定/proc/sys/kernel/yama/ptrace_scopeする0と、straceプログラムを使用できるようになります。さらにスイッチを使用して無音にすることができるため、実際には受動的に待機します。

strace -qqe '' -p <PID>

1
良いですね!ただし、2つの異なる場所から特定のPIDに接続することはできないようです(Operation not permitted2番目のstraceインスタンスを取得します)。確認できますか?
eudoxos 2014

@eudoxosはい、ptraceのマンページには次のように書かれています(...)"tracee" always means "(one) thread"(そして私はあなたが言及したエラーを確認します)。この方法でより多くのプロセスを待機させるには、チェーンを作成する必要があります。
emu 2014

2

ブロッキングソリューション

waitすべてのプロセスの終了を待機するには、ループを使用します。

function anywait()
{

    for pid in "$@"
    do
        wait $pid
        echo "Process $pid terminated"
    done
    echo 'All processes terminated'
}

この関数は、すべてのプロセスが終了するとすぐに終了します。これが最も効率的なソリューションです。

非ブロッキングソリューション

使用するkill -0すべてのプロセスを終了さ+チェックの間に何かをするのを待つために、ループの中で:

function anywait_w_status()
{
    for pid in "$@"
    do
        while kill -0 "$pid"
        do
            echo "Process $pid still running..."
            sleep 1
        done
    done
    echo 'All processes terminated'
}

sleep高いCPU使用率を回避する必要があるため、反応時間は時間とともに減少しました。

現実的な使い方:

すべてのプロセスの終了を待つ+ 実行中のすべての PID についてユーザーに通知します。

function anywait_w_status2()
{
    while true
    do
        alive_pids=()
        for pid in "$@"
        do
            kill -0 "$pid" 2>/dev/null \
                && alive_pids+="$pid "
        done

        if [ ${#alive_pids[@]} -eq 0 ]
        then
            break
        fi

        echo "Process(es) still running... ${alive_pids[@]}"
        sleep 1
    done
    echo 'All processes terminated'
}

ノート

これらの関数は$@、BASH配列として引数を介してPIDを取得します。


2

同じ問題があったので、プロセスを強制終了し、各プロセスがPROCファイルシステムの使用を完了するのを待つ問題を解決しました。

while [ -e /proc/${pid} ]; do sleep 0.1; done

投票は非常に悪いです、あなたは警察から訪問を受けることができます:)
Alexander Mills

2

プロセスが完了するのを待つ組み込みの機能はありません。

kill -0見つかった任意のPIDに送信できるので、ゾンビやまだ表示されているものに困惑することはありませんps(まだを使用してPIDリストを取得していますps)。


1

inotifywaitを使用して、プロセスが終了したときに閉じられるファイルを監視します。例(Linuxの場合):

yourproc >logfile.log & disown
inotifywait -q -e close logfile.log

-eは待機するイベントを指定し、-qは終了時にのみ最小限の出力を意味します。この場合は次のようになります。

logfile.log CLOSE_WRITE,CLOSE

単一の待機コマンドを使用して、複数のプロセスを待機できます。

yourproc1 >logfile1.log & disown
yourproc2 >logfile2.log & disown
yourproc3 >logfile3.log & disown
inotifywait -q -e close logfile1.log logfile2.log logfile3.log

inotifywaitの出力文字列から、どのプロセスが終了したかがわかります。これは「実際の」ファイルでのみ機能し、/ proc /にあるものでは機能しません


0

OSXのようなシステムでは、pgrepがない可能性があるため、名前でプロセスを検索するときにこの方法を試すことができます。

while ps axg | grep process_name$ > /dev/null; do sleep 1; done

$プロセス名性を保証の末尾シンボルはgrepのマッチのみPS出力のラインの終わりではなく、それ自体にPROCESS_NAMEこと。


恐ろしい:コマンドラインのどこかにその名前を持つ複数のプロセスがあり、独自のgrepが含まれている可能性があります。代わりにリダイレクトする/dev/null-qして使用する必要がありますgrep。ループがスリープしているときにプロセスの別のインスタンスが開始された可能性があり、あなたはそれを決して知ることができません...
ミハイルT.

同様のアプローチが他の人から提案されているので、なぜあなたがこの回答を「恐ろしい」と特定したのかわかりませんか?一方で-q、私は私の答えに特異的に終端述べたように提案、有効である$「コマンドラインでどこかに」名前と一致しません。またそれは、自分自身にマッチしますgrepの手段を。あなたは実際にOSXでそれを試しましたか?
Pierz

0

Rauno Palosaariのに対するソリューションはTimeout in Seconds Darwin、GNUを持たないUNIXライクなOS tail(これはに固有ではありませんDarwin)に対する優れた回避策です。ただし、UNIXライクなオペレーティングシステムの古さに応じて、提供されるコマンドラインは必要以上に複雑で、失敗する可能性があります。

lsof -p $pid +r 1m%s -t | grep -qm1 $(date -v+${timeout}S +%s 2>/dev/null || echo INF)

少なくとも1つの古いUNIXでは、lsof引数+r 1m%sは(スーパーユーザーであっても)失敗します。

lsof: can't read kernel name list.

m%s出力フォーマット仕様です。より単純なポストプロセッサはそれを必要としません。たとえば、次のコマンドは、PID 5959で最大5秒間待機します。

lsof -p 5959 +r 1 | awk '/^=/ { if (T++ >= 5) { exit 1 } }'

この例では、5秒が経過する前にPID 5959が自動的に終了すると、${?}です0。そうでない場合は5秒後に${?}戻ります1

+r 1では、1はポーリング間隔(秒単位)であるため、状況に合わせて変更される可能性があることを明記しておくとよいでしょう。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.