web-dev-qa-db-ja.com

bashスクリプトでクロック信号をトラップできますか?

スクリプト内に5分の信号をトラップできますか?私はこのようなものを想像します、

function dosomething {
   echo "It's been 5 minutes."
}

trap dosomething SIGNAL-EVERY-5-MINUTES

while true
do
    sleep 10
done

この例には意味がないことに注意してください。 x回ごとに何かをトラップすることが可能かどうかを尋ねるだけです。


EDIT: @ vinc17の回答に基づいて、メインシェルからのsleepコマンドがUSR1信号によって中断されていないことに気づきました。それが、私がやりたいことです。 。

#!/bin/bash
time=10

(
  set -e
  while true
  do
    sleep 30
    # Since this is run as a subshell (instead of an external command),
    # the parent pid is $$, not $PPID.
    kill -USR1 $$
  done
) &

finish() {
   kill $!
   echo "Closing."
   exit 0
}

changetime() {
   echo "Signal USR1 received."
   time=$(( $RANDOM % 8 + 1))
}

trap changetime USR1
trap finish SIGINT

while true
do
  echo "before sleep"
  sleep $time
  echo "after sleep"
done

出力、

before sleep
Signal USR1 received.
after sleep
before sleep
Signal USR1 received.
after sleep
before sleep
Signal USR1 received.
Closing.

EDIT2:上記の例を編集した結果、同じ出力が得られましたが、もう1つ問題があります。スリープ時間はUSR1トラップ関数によって変更されます。そのため、現在、30秒ごとに1から8までのランダムな時間が選択されています。したがって、シグナルが時間を変更したときに、メインスクリプトからsleepを強制終了する必要があります。私はそれが意味をなさないと主張しますが、それが可能かどうかを知る必要があります。

3
whitenoisedb

はい。最も簡単な方法は、質問で最初に説明したとおりにそれを行うことです。それはその一部です何か 5分後にプロセスにシグナルを送信するために、別の子プロセスを生成する必要があります。 @ vinc17の本からページを取り、スパンも5秒に短縮します。

trap 'ME=$$
     : $( (sleep 5
     ps -p $ME >/dev/null 2>&1 && { 
        echo hey >/dev/tty
        kill -USR1 $ME
     })&)
' USR1; kill -USR1 $$

そうすれば、ゾンビになるように要求するだけの長期的なプロセスはありません。代わりに、間隔ごとに親のPIDへの新しい参照を取得する新しい単一目的のカウンターがあります。また、親が離れてから死亡した場合に、子供が1つの仕事をしようとしないことを確認するために、少しチェックを組み込みます。

それを実行すると、5秒ごとに次のようになります。

hey
hey
hey
hey
hey
hey
hey
hey
hey
hey

この例でも、その間にターミナルがハングする可能性があります。そうしないと、プロセス全体をバックグラウンドで実行しようとしても何も起こりません。これは、/dev/ttyが保持されているためです。バックグラウンドですべてを実行する場合は、代わりにファイルを選択します。それ以外の場合は、直接stty呼び出しを実行し、必要に応じてttyを再実行します。

2
mikeserv