web-dev-qa-db-ja.com

STDINが開いているかどうかを定期的に確認するにはどうすればよいですか?

一部のプログラムは、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.shSTDINが閉じられると、そのブロッキングreadが終了し、バックグラウンド処理されたプロセスが終了します。

ただし、このアプローチには欠点があります。 バックグラウンドプロセスが他の理由で終了した場合、wrapper.shは気付かないため、Erlang/Elixirも気づきません。

このスクリプトを変更して、次のことを実行したいと思います。

ループ内:

  • STDINが閉じている場合は、$pidを強制終了します
  • $pidが無効な場合は、終了します
  • それ以外の場合は、短時間眠ります

誰かがこれを行う方法を提案できますか?

3
Nathan Long

$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
3
bitwalker

シェルがdash/busyboxksh93、またはzshの場合、SIGCHLDにトラップを設定できます。

#! /bin/sh
trap exit CHLD
"$@" &
while read line; do :; done
kill $!
wait

これは、"$@" &プロセスが終了するか、readEOFを取得するとすぐに終了します。

しかし、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でも機能するようです。

1
mosvy