パイプラインバックグラウンドプロセスのPIDを取得する安定した方法

パイプラインバックグラウンドプロセスのPIDを取得する安定した方法

Bashでは、バックグラウンドジョブとして一緒に作成された別のプロセスにパイプされているプロセスのPIDを取得する必要があります。以前は単に依存していましたが、プロセスを見つけるまでに2秒以上の遅延がある可能性があることがわかりpgrepました。pgrep

#!/bin/bash
cmd1 | cmd2 &
pid=$(pgrep cmd1) # emtpy in about 1/10

この問題について私が見つけたいくつかの一般的なアドバイスは、単純なパイプ(cmd1 >(cmd2) & pid=$!)の代わりにプロセス置換を使用するか、jobs組み込み関数を使用することです。プロセスの置き換えは(フルランタイム中に)フルサブシェルを実行するので、今はそれを使用することをお勧めしますがjobs、同じ間違いを2回繰り返さないことを願っています...

両方のプロセスを作成してすぐにルックアップを実行すると、両方のプロセスを知っているとjobs100%信頼できますか?

#!/bin/bash
cmd1 | cmd2 &
pid=$(jobs -p %cmd1) # 10/10?

これはバックグラウンドでジョブを実行するためである可能性があり、奇妙なことになるかもしれませんが、set -x次の例では通常実行されるコマンドをランダムな順序で一覧表示します。jobs出力現れるjobsこれまでは正確ですが、実行される可能性は完全に排除したいと思います。今後ジョブが開始されました(または少なくともjobs2つのプロセスをリストできません)! ?

#!/bin/bash
set -x
tail -f /dev/null | cat &
jobs -l
kill %tail

例:

+ jobs -l
[1]+ 2802325 Running                 tail -f /dev/null
     2802326                       | cat &
+ tail -f /dev/null
+ kill %tail

同様に、プロセス交換の場合でもpid=$!常に動作すると信じられますか?正確に、「最も最近実行されたバックグラウンド(非同期)コマンドのプロセスIDに拡張」するように設計されていますか?

ベストアンサー1

バックグラウンドジョブがフォームのパイプラインであっても、それはcmd1 | cmd2まだ単一のバックグラウンドジョブです。いつcmd1始まるかを知る方法はありません。

それぞれは&バックグラウンドジョブを生成します。cmd &返されると、シェルはバックグラウンドジョブであるlistscmd & jobsを認識しますcmd。実行中のプロセスIDにcmd & pid=$!設定します。pidcmd

パイプはcmd1 | cmd22つの異なるサブプロセスを生成します。 1つは実行しcmd1、もう1つは実行しますcmd2。どちらのプロセスも、バックグラウンドジョブを実行する子プロセスの子プロセスです。プロセスツリーは次のとおりですbash -c '{ sleep 123 | sleep 456; } & jobs -p; sleep 789'

 PID PPID CMD
 268  265  |   \_ bash -c { sleep 123 | sleep 456; } & sleep 789
 269  268  |       \_ bash -c { sleep 123 | sleep 456; } & sleep 789
 270  269  |       |   \_ sleep 123
 271  269  |       |   \_ sleep 456
 272  268  |       \_ sleep 789

268は元のbashプロセスです。 269は印刷用のバックグラウンドジョブですjobs -p。 270と271はパイプの左右にあり、両方のサブプロセスはバックグラウンドジョブ(269)のメインプロセスです。

私がテストしたbashバージョン(Linuxでは5.0.17)は中cmd1 | cmd2 &括弧なしで最適化されました。この場合、パイプラインの左側はバックグラウンドジョブと同じプロセスで実行されます。

 PID PPID CMD
 392  389  |   \_ bash -c sleep 123 | sleep 456 & jobs -p; sleep 789
 393  392  |       \_ sleep 123
 394  392  |       \_ sleep 456
 395  392  |       \_ sleep 789

bashバージョン間、さらにはプラットフォーム、ディストリビューション、libcバージョンなどの間でも、この動作が安定していると信じることはできません。

jobs -p %cmd1コードがで始まる採用情報を見つけますcmd1。見つけたのはcmd1 | cmd2jobs -p %?cmd2bashの組み込み機能を使用してcmd1実行中のプロセスIDにアクセスする方法はありません。cmd2

cmd1開始されたことを確認する必要がある場合は、プロセス置換を使用してください。

cmd1 >(cmd2)

cmd2いつ開始して終了するのかわかりません。

cmd1いつ開始して終了するかを知る必要がある場合は、cmd2同時に動作させ、名前付きパイプを介して通信する必要があります。

tmp=$(mktemp -d) # Remove this in cleanup code
mkfifo "$tmp/pipe"
cmd1 >"$tmp/pipe" & pid1=$!
cmd2 <"$tmp/pipe" & pid2=$!

このjobsコマンドはスクリプトではあまり役に立ちません。$!バックグラウンドジョブのPIDを記憶するために使用されます。

1または少なくともそうする必要があります。私のバージョンでは作業仕様が不明だと文句を言いますが、作業が一つしかないとされているのでバグであること間違いありません。

おすすめ記事