デーモンをバックグラウンドで送信し、デーモンが次の行を含む特定の行をstderrに出力する場合にのみ、スクリプトの実行を続けたいと思います。
# Fictional daemon
{
for x in {1..9}; do
sleep 1
if [ "$x" != "5" ]; then
echo $x 1>&2
else
echo now 1>&2
fi
done
} &
# Daemon PID
PID="$!"
# Wait until the particular output...
until { read -r line && grep -q "now" <<<"$line"; } do
sleep 1
done </proc/$PID/fd/2
#
# Do more stuff...
#
fg
ベストアンサー1
そのmydaemon
動作は次のとおりです。
#! /bin/sh -
i=1 stage=init
while true; do
if [ "$i" -eq 5 ]; then
echo >&2 ready
stage=working
fi
echo >&2 "$stage $i"
sleep 1
i=$((i + 1))
done
つまり、初期化してready
準備ができたら出力し、作業を続けるのに4秒かかります。すべて同じプロセスでstart-mydaemon
次のスクリプトを作成できます。
#! /bin/sh -
DAEMON=./mydaemon
LOGFILE=mydaemon.log
umask 027
: >> "$LOGFILE" # ensures it exists
pid=$(
sh -c 'echo "$$"; exec "$0" "$@"' tail -fn0 -- "$LOGFILE" | {
IFS= read -r tail_pid
export LOGFILE DAEMON
setsid -f sh -c '
echo "$$"
exec "$DAEMON" < /dev/null >> "$LOGFILE" 2>&1'
grep -q ready
kill -s PIPE "$tail_pid"
}
)
printf '%s\n' "$DAEMON started in process $pid and now ready"
$ time ./start-mydaemon
./mydaemon started in process 230254 and now ready
./start-mydaemon 0.01s user 0.01s system 0% cpu 4.029 total
$ ps -fjC mydaemon
UID PID PPID PGID SID C STIME TTY TIME CMD
chazelas 230254 6175 230254 230254 0 10:28 ? 00:00:00 /bin/sh - ./mydaemon
start-mydaemon
mydaemon
準備ができたことを示すまで返されません。mydaemon
stdoutとstderrはログファイルに移動し、stdinは/dev/null
。setsid -f
(標準コマンドではありませんが、ほとんどのLinuxディストリビューションで利用可能です)で、デーモンがターミナルから切り離されていることを確認する必要があります(ターミナルから起動した場合)。
mydaemon
ただし、初期化が失敗して書き込まずに終了すると、ready
スクリプトはready
決して来ない(またはmydaemon
次に正常に起動したときに来る)何かを永遠に待つことに注意してください。
またsh -c ...tail
、デーモンが同時に起動することに注意してください。初期化して最初から印刷してmydaemon
ログファイルの終わりを見つけた場合、メッセージがありません。ready
tail
tail
ready
次の方法でこれらの問題を解決できます。
#! /bin/sh -
DAEMON=./mydaemon
LOGFILE=mydaemon.log
export DAEMON LOGFILE
umask 027
died=false ready=false canary_pid= tail_pid= daemon_pid=
: >> "$LOGFILE" # ensures it exists
{
exec 3>&1
{
tail -c1 <&5 5<&- > /dev/null # skip to the end synchronously
(
sh -c 'echo "tail_pid=$$" >&3; exec tail -fn+1' |
{ grep -q ready && echo ready=true; }
) <&5 5<&- &
} 5< "$LOGFILE"
setsid -f sh -c '
echo "daemon_pid=$$" >&3
exec "$DAEMON" < /dev/null 3>&- 4>&1 >> "$LOGFILE" 2>&1' 4>&1 |
(read anything; echo died=true) &
echo "canary_pid=$!"
} | {
while
IFS= read -r line &&
eval "$line" &&
! "$died" &&
! { [ -n "$daemon_pid" ] && "$ready" ; }
do
continue
done
if "$ready"; then
printf '%s\n' "$DAEMON started in process $daemon_pid and now ready"
else
printf >&2 '%s\n' "$DAEMON failed to start"
fi
kill -s PIPE "$tail_pid" "$canary_pid" 2> /dev/null
"$ready"
}
しかし、これはかなり複雑になり始めました。またtail -f
、Linuxのstdinで実行されるので、ファイルに新しいデータがあるかどうかを検出するためにinotifyを使用せずに一般的な方法を使用します。毎秒確認してくださいready
これは、ログファイルで検出するのにさらに時間がかかることを意味します。