やりたいこと
次のシェルスクリプトにおいて、 longjob
の終了と同時に tail -f
を終了させたい。
# !/bin/bash
longjob &>log & disown
tail -f log
条件:
- 1つのシェルスクリプト内で行いたい。
- 端末が終了しても
longjob
は継続させたい (つまり、& disown
は譲れない)。 -
Ctrl-c
の押下時にtail
は終了させたいがlongjob
は終了させたくない。
解決策1
tail -f
の開始前に、以下を行うバックグラウンドプロセスをあらかじめ起動しておく。
-
longjob
が終了するまで待機。
-
tail
のプロセスを kill。
# !/bin/bash
longjob &>log & disown
(
# set PID_JOB
PID_JOB=$!
# set PID_TAIL
while test -z $PID_TAIL; do PID_TAIL=$(ps --ppid=$$ | grep tail | awk '$0=$1'); done
# wait PID_JOB
while ps -p $PID_JOB >/dev/null; do sleep 0.5; done
# kill PID_TAIL
kill -INT $PID_TAIL
) &
tail -f log
-
tail
の開始前にPID_TAIL=$(...)
の部分が実行された場合、$PID_TAIL
は空文字になる。PID_TAIL
を確実に設定するために、# set PID_TAIL
の部分はwhile
で回している。 - 問題点:
longjob
が終了するとkill -INT
が発砲され、tail
が異常終了する。
解決策2
tail
をバックグラウンドで一旦起動しおいて fg
で処理を戻す。
# !/bin/bash -i
longjob &>log & disown
PID_JOB=$!
tail -f log &
PID_TAIL=$!
(
# wait PID_JOB
while ps -p $PID_JOB >/dev/null; do sleep 0.5; done
# kill PID_TAIL
kill -INT $PID_TAIL 2>/dev/null
) &
fg 1 >/dev/null
- 注意: シェバングが
#!/bin/bash -i
。- fg はインタラクティブシェルにおいて利用可。
- インタラクティブシェルとしてシェルスクリプトを起動するため。
-
longjob
終了前にCtrl-c
した場合にkill
が失敗するため、エラーを捨てる。 -
fg
したときにtail -f log
と表示されるのを防ぐため、標準出力を捨てる。
うまく行かない方法
tee & trap
# Ctrl-c で longjob が終了してしまう
longjob 2>&1 | tee log
# Ctrl-c で終了できない
trap '' 1 2 15
nohup longjob 2>&1 </dev/null | tee log
# longjob は最後まで実行されるが、Ctrl-c で teeが終了し、それ以降のログが記録されない
# longjob に "trap '' 1 2 15" を記述した上で
nohup longjob 2>&1 </dev/null | tee log
longjob に trap '' 1 2 15
と tee log
を記述した上で解決策2のようなコードを書けばできるが美しくはない。
wait
# wait PID_JOB
の部分を wait $PID_JOB
とするとこうなる。
wait: pid 5734 is not a child of this shell