Bashには、プロセスの終了を待つ組み込みの機能はありますか?
このwait
コマンドでは、子プロセスが終了するのを待つだけです。スクリプトを続行する前に、プロセスが完了するのを待つ方法があるかどうか知りたいのですが。
これを行うための機械的な方法は次のとおりですが、Bashに組み込みの機能があるかどうかを知りたいです。
while ps -p `cat $PID_FILE` > /dev/null; do sleep 1; done
Bashには、プロセスの終了を待つ組み込みの機能はありますか?
このwait
コマンドでは、子プロセスが終了するのを待つだけです。スクリプトを続行する前に、プロセスが完了するのを待つ方法があるかどうか知りたいのですが。
これを行うための機械的な方法は次のとおりですが、Bashに組み込みの機能があるかどうかを知りたいです。
while ps -p `cat $PID_FILE` > /dev/null; do sleep 1; done
回答:
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)
tail
するか知っていただろう。
tail
kill(pid, SIG_0)
プロセスをポーリングして内部で動作します(を使用して検出されますstrace
)。
+r 1
タイムアウトを使用していることに注意してください。個人的には、ポーリングを使用しないMacOSのソリューションを探しています。
tail
ラインを持っていますkill (pid, 0) != 0 && errno != EPERM
。
caffeinate -w $pid
は、トリックを実行します。
ビルトインはありません。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 "
。
sleep 0.5
、withでプロセス$pid
が停止し、同じプロセスで別のプロセスが作成されることがあります$pid
。そして、同じ2つの異なるプロセス(またはそれ以上)を待つことになり$pid
ます。
プロセスがルート(またはその他)によって所有されている場合、「kill -0」は機能しないので、pgrepを使用して次のように思い付きました。
while pgrep -u root process_name > /dev/null; do sleep 1; done
これには、おそらくゾンビプロセスと一致するという欠点があります。
kill(pid, sig=0)
呼び出し元のプロセスにkillする特権がない場合、システムコールは失敗します。したがって、/ bin / kill -0および "kill -0"(bashビルトイン)も同じ条件で失敗します。
この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自体には、非子プロセスの終了を監視するシステムコールがありません。
grep /proc/$PID/status
二重引用符(bash: test: argument expected
)で囲む必要がありましたが
while s=`ps -p $PID -o s=` && [[ "$s" && "$s" != 'Z' ]]; do sleep 1; done
ps
、どちら-p
でもないかs=
、サポートされていません
FreeBSDとSolarisには、この便利なpwait(1)
ユーティリティがあります。これは、ユーザーの希望どおりに機能します。
他の最近のOSにも必要なシステムコールがあります(たとえば、MacOSはBSDを実装kqueue
しています)が、すべてがコマンドラインから使用できるわけではありません。
> BSD and Solaris
:頭に浮かぶ3つの大きなBSDの検査。Man.openbsd.orgで簡単に確認できるように、OpenBSDもNetBSDも(manページに)この機能はありません。FreeBSDだけにあります。
kqueue
しているので、FreeBSDのコンパイルpwait(1)
は簡単です。他のBSDの機能がインポートされないのはなぜ私が逃げるのか...
plink me@oracle box -pw redacted "pwait 6998";email -b -s "It's done" etc
今から数時間ではなく今すぐ家に帰ることを許可しました。
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.
wait
引数がないと、子プロセスが完了するまでプロセスがブロックされます。正直なところ、常にシステムプロセスが進行しているため、プロセスを待つ必要はありません。
sleep 1000
ctrl-z
wait [sleep pid]
すぐに戻る
これらのソリューションはすべてUbuntu 14.04でテストされています。
解決策1(psコマンドを使用): Pierzの回答を合計するために、私は次のことをお勧めします。
while ps axg | grep -vw grep | grep -w process_name > /dev/null; do sleep 1; done
この場合、grep -vw grep
grepが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に置き換えます。
grep -v grep
パイプラインは大規模なアンチパターンであり、これは同じ名前の無関係なプロセスがないことを前提としています。代わりにPIDがわかっている場合、これは適切に機能するソリューションに適応できます。
-w
問題をgrep -v grep
ある程度回避するためにフラグを追加しました。また、あなたのコメントに基づいて2つの解決策を追加しました。
さて、答えはそうです-いいえ、組み込みのツールはありません。
に設定/proc/sys/kernel/yama/ptrace_scope
する0
と、strace
プログラムを使用できるようになります。さらにスイッチを使用して無音にすることができるため、実際には受動的に待機します。
strace -qqe '' -p <PID>
Operation not permitted
2番目のstraceインスタンスを取得します)。確認できますか?
(...)"tracee" always means "(one) thread"
(そして私はあなたが言及したエラーを確認します)。この方法でより多くのプロセスを待機させるには、チェーンを作成する必要があります。
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を取得します。
同じ問題があったので、プロセスを強制終了し、各プロセスがPROCファイルシステムの使用を完了するのを待つ問題を解決しました。
while [ -e /proc/${pid} ]; do sleep 0.1; done
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 /にあるものでは機能しません
OSXのようなシステムでは、pgrepがない可能性があるため、名前でプロセスを検索するときにこの方法を試すことができます。
while ps axg | grep process_name$ > /dev/null; do sleep 1; done
$
プロセス名性を保証の末尾シンボルはgrepのマッチのみPS出力のラインの終わりではなく、それ自体にPROCESS_NAMEこと。
/dev/null
、-q
して使用する必要がありますgrep
。ループがスリープしているときにプロセスの別のインスタンスが開始された可能性があり、あなたはそれを決して知ることができません...
-q
、私は私の答えに特異的に終端述べたように提案、有効である$
「コマンドラインでどこかに」名前と一致しません。またそれは、自分自身にマッチしますgrepの手段を。あなたは実際にOSXでそれを試しましたか?
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
はポーリング間隔(秒単位)であるため、状況に合わせて変更される可能性があることを明記しておくとよいでしょう。