(完成のために:ストーリー; 私がこの質問をしている理由を知りたくない場合は、これを読む必要はありません :カメラモーション検出機能を備えたWebカメラデーモンアプリケーション(「モーション」と呼ばれる)を入手しました。このデーモンは、カメラによってモーションが検出されたときにカスタムコマンドをトリガーできます。さらに、Androidモバイルにプッシュ通知を簡単に送信できる小さなコマンドラインプログラムをインストールしました。動きを検出したときにそのpush-message-commandを実行するようにcam-demonを構成しました。)
問題:悪魔は、モーションに必要なすべてのフレームでカスタムコマンドをトリガーし、5秒間の連続モーションがあると、大量のプッシュ通知が表示されます(カメラのフレームレートは10に設定されています)。写真/秒なので、モーションと見なされる毎秒10回のプッシュメッセージを取得します。
過去x秒/分ですでに実行されている場合は、そのpush-msgコマンドをブロックするスクリプトまたはプログラムが必要です。
私は次のようなものを想像することができます:
$ proxy_command.sh --block_time=10s --command==androidpush -msg "There was a motion in your flat!"
これを解決する簡単でエレガントな方法を見つけることができませんでした(タイムスタンプ付きのファイルを書き込んでから、そのファイルの内容を確認する必要があります)。どちらも同様の問題を抱えている人を見つけることができませんでした。
上記のような私の問題をできるだけ簡単に解決するようなコマンドプロキシや何かがありますか?
承認された回答:
最後の呼び出し時刻をファイルの最終変更時刻として保存する
perl
#! /usr/bin/perl
# usage: cmd file seconds cmd args
$file = shift @ARGV;
$time = shift @ARGV;
$age = -M $file;
exit 3 if defined($age) && 86400 * $age < $time;
open $fh, ">>", $file ||
die "Can't open $file: $!\n";
utime undef, undef, $fh;
exec @ARGV;
次のように使用します:
that-script /some/file 10 androidpush -msg 'There was a motion in your flat!'
最後の実行時刻を/some/file
の最終変更時刻として記録します そのファイルの経過時間が指定された時間よりも短い場合、コマンドは実行されません。
シェル
BSDまたはGNUの場合find
、あなたはそれを行うことができます:
#! /bin/sh -
file=${1?} time=${2?}
shift 2
[ "$(find -- "$file" -prune -newermt "$time ago")" ] && exit 3
touch -- "$file"
exec "[email protected]"
実行するには:
that-script /some/file 10sec androidpush -msg 'There was a motion in your flat!'
いずれの場合も、前回の実行に関する情報を、次の実行のために保持される場所に保存する必要があります。ファイルシステムはそのための明白な場所です。それはあなたがあなた自身のためにいくつかの領域を予約することができるものです。
特定の名前のスリーププロセス
別の名前空間は、たとえばプロセス名である可能性があります:
#! /bin/bash -
label=$0-$(logname)@${1?} time=${2?}
shift 2
pgrep -f "^$label" > /dev/null 2>&1 && exit 3
(exec -a "$label" sleep "$time") &
exec "[email protected]"
使用目的:
that-script motion 10 androidpush -msg 'There was a motion in your flat!'
(sleep
を想定 argv[0]
を気にしない実装 。
bash
を使用しています sh
の代わりに exec -a arg0
の場合 。 bash
がない場合 、ksh93
を含むサポートする他のシェル 、mksh
、yash
およびzsh
。または、perl
に戻すこともできます もう一度。
名前空間は予約されていないことに注意してください。 (~/.lastrun
を使用するのとは対照的に、別のユーザーが同じ名前のプロセスを作成することを妨げるものは何もありません。 ただし、ここでは、これらのスクリプトはすべて同じmotion
によって開始されます。 プロセスの場合、プロセスの検索を同じ親プロセスIDを持つプロセスに制限できます:
すべてのスクリプトが常に同じプロセスで開始される場合の改善
#! /bin/bash -
label=$0-${1?} time=${2?}
shift 2
pgrep -P "$PPID" -f "^$label" > /dev/null 2>&1 && exit 3
(exec -a "$label" sleep "$time") &
exec "[email protected]"
Linuxのみ:タイムアウト付きのカーネルキーを使用
Linuxでは、ユーザーのセッションキーリングでキーを使用することもできます。これは実際にはそのために設計されたものではありませんが、これらのキーは時間の経過とともに存続し、タイムアウトを与えることができるため、ここでは便利です:
#! /bin/sh -
key=$0-${1?} time=${2?}
shift 2
keyctl search @us user "$key" > /dev/null 2>&1 && exit 3
key=$(keyctl add user "$key" x @us) || exit
keyctl timeout "$key" "$time" || exit
exec "[email protected]"
さて、あなたがそのスクリプトの2つのインスタンスを同時に実行していないので、あなたのケースでは実際には問題ではありませんが、それらはすべて競合状態を持っているため、2つのインスタンスが実行されないことは保証されません。指定された時間内に実行します。 2つのインスタンスが同時に実行される場合、どちらかがリセットする前に、両方が条件に問題がないことを確認できます。
競合状態を回避するためにファイルをロックする
これを回避するアプローチは、sleep
を使用することです。 プロセスはファイルのロックを保持します:
#! /bin/sh -
file=${1?} time=${2?}
shift 2
{
flock -n 3 || exit 3
sleep "$time" 3>&3 &
exec "[email protected]"
} 3<> "$file"
(さらに、ここでは、両方のsleep
また、実行中のコマンドはロックを保持し、sleep
よりも実行に時間がかかる場合でも、コマンドの2つのインスタンスが同時に実行されないようにします。 コマンド)。