Quantcast
Channel: Bashタグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 2867

tail -fをログ出力プロセスの終了と同時に終了

$
0
0

やりたいこと

次のシェルスクリプトにおいて、 longjobの終了と同時に tail -fを終了させたい。

#!/bin/bash

longjob &>log & disown
tail -f log

条件:

  • 1つのシェルスクリプト内で行いたい。
  • 端末が終了しても longjobは継続させたい (つまり、& disownは譲れない)。
  • Ctrl-cの押下時に tailは終了させたいが longjobは終了させたくない。

解決策1

tail -fの開始前に、以下を行うバックグラウンドプロセスをあらかじめ起動しておく。

  1. longjobが終了するまで待機。
  2. 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 15tee logを記述した上で解決策2のようなコードを書けばできるが美しくはない。

wait

# wait PID_JOBの部分を wait $PID_JOBとするとこうなる。

wait: pid 5734 is not a child of this shell

Viewing all articles
Browse latest Browse all 2867

Trending Articles