一部のプログラムは、STDIN
が閉じられると終了します。これを行うものは、「ポート」を介したErlang/Elixirの監視でうまく機能します。
そうでない人のために、 Elixir docs このラッパースクリプトを提案します:
#!/bin/bash
# wrapper.sh
"$@" &
pid=$!
while read line ; do
:
done
kill -KILL $pid
これにより、wrapper.sh my_script arg1 arg2
を呼び出すことができます。 wrapper.sh
は指定されたプログラムを開始してバックグラウンド処理し、wrapper.sh
のSTDIN
が閉じられると、そのブロッキングread
が終了し、バックグラウンド処理されたプロセスが終了します。
ただし、このアプローチには欠点があります。 バックグラウンドプロセスが他の理由で終了した場合、wrapper.sh
は気付かないため、Erlang/Elixirも気づきません。
このスクリプトを変更して、次のことを実行したいと思います。
ループ内:
STDIN
が閉じている場合は、$pid
を強制終了します$pid
が無効な場合は、終了します誰かがこれを行う方法を提案できますか?
$pid
が次のように死んでいるかどうかを確認できます。
while kill -0 $pid >/dev/null; do
# $pid is still alive
done
# $pid is dead or you lack permissions to send signals to it
シェルがdash
/busybox
、ksh93
、またはzsh
の場合、SIGCHLD
にトラップを設定できます。
#! /bin/sh
trap exit CHLD
"$@" &
while read line; do :; done
kill $!
wait
これは、"$@" &
プロセスが終了するか、read
がEOF
を取得するとすぐに終了します。
しかし、bash
には厄介なread
が組み込まれています。これは、シグナルによって中断されたときに 再起動 の場所にあるため、より不格好なものが必要です。バックグラウンドプロセスが終了時に親に終了シグナルを送信するように調整できます。
#! /bin/sh
{ "$@"; kill $$; } &
while read line; do :; done
pkill -P $!
kill $!
wait
"$@"
は-P
コマンドではなく、その親を参照するため、これは$!
コマンドを待機するために別のプロセスを浪費し、pkill
の"$@"
(親pid)セレクターを使用する必要があります。 kill $!
がたまたま組み込みである場合に備えて、追加の"$@"
が引き続き必要です。スクリプトにはジョブ制御がないことに注意してください。すべてのプロセス(&
で始まるプロセスを含む)は、同じプロセスグループで実行されます。
TERM
信号では不十分な場合は、どこでもp/kill -KILL
を使用できます。
新しい(> = 4.0)バージョンのbashでのみ機能するもう1つのさらに厄介な回避策は、read
の非標準の-t
(タイムアウト)オプションを使用し、read
が1を返すという事実を利用することです。 EOF
およびタイムアウト時のステータス> 128:
#! /bin/bash
"$@" &
while :; do
read -t 1 line
case $? in
0) ;;
1) kill $!; wait; exit;;
*) kill -0 $! || exit;;
esac
done 2>/dev/null
これはどういうわけかmksh
でも機能するようです。