(Nur zur Vervollständigung:die Geschichte; Sie brauchen dies nicht zu lesen, wenn Sie nicht wissen wollen, warum ich diese Frage stelle :Ich habe eine Webcam-Dämon-Anwendung (namens „Bewegung“) mit Kamerabewegungserkennung. Dieser Dämon kann einen benutzerdefinierten Befehl auslösen, wenn die Kamera eine Bewegung erkennt. Außerdem habe ich ein winziges Kommandozeilenprogramm installiert, das ganz einfach Push-Benachrichtigungen an mein Android-Handy senden kann. Ich habe den Cam-Dämon so konfiguriert, dass er diesen Push-Message-Befehl ausführt, wenn er eine Bewegung erkennt.)
Das Problem:Der Dämon löst diesen benutzerdefinierten Befehl mit jedem Frame aus, den er für eine Bewegung aufnimmt, was mich zu einer massiven Menge an Push-Benachrichtigungen führt, wenn es eine kontinuierliche Bewegung für nur – sagen wir – 5 Sekunden gibt (die Kamera-Framerate ist auf 10 eingestellt Bilder/Sekunde, also bekomme ich jede Sekunde 10 Push-Nachrichten, die als Bewegung gewertet werden).
Ich brauche ein Skript oder Programm, das diesen Push-msg-Befehl blockiert, wenn er in den letzten x Sekunden / Minuten bereits ausgeführt wurde.
Ich könnte mir sowas vorstellen wie:
$ proxy_command.sh --block_time=10s --command==androidpush -msg "There was a motion in your flat!"
Ich konnte keinen einfachen/noch eleganten Weg finden, dies zu lösen (ohne Dateien mit Zeitstempeln zu schreiben und dann den Inhalt dieser Dateien zu überprüfen). Ich konnte auch niemanden finden, der ein ähnliches Problem hat.
Gibt es eine Art Befehlsproxy oder etwas, das mein oben beschriebenes Problem so einfach wie möglich lösen würde?
Akzeptierte Antwort:
Speichern der letzten Aufrufzeit als letzte Änderungszeit einer Datei
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;
Zu verwenden wie:
that-script /some/file 10 androidpush -msg 'There was a motion in your flat!'
Es zeichnet die Zeit der letzten Ausführung als letzte Änderungszeit von /some/file
auf und führt den Befehl nicht aus, wenn das Alter dieser Datei kleiner als die angegebene Zeit ist.
Schale
Mit BSD oder GNU find
, könnten Sie es tun mit:
#! /bin/sh -
file=${1?} time=${2?}
shift 2
[ "$(find -- "$file" -prune -newermt "$time ago")" ] && exit 3
touch -- "$file"
exec "[email protected]"
Ausführen als:
that-script /some/file 10sec androidpush -msg 'There was a motion in your flat!'
In jedem Fall müssen Sie die Informationen über den letzten Lauf an einem Ort speichern, der für die nächsten Läufe bestehen bleibt. Das Dateisystem ist dafür ein naheliegender Ort. Dort können Sie einen Bereich für sich selbst reservieren.
Sleep-Prozess mit spezifischem Namen
Ein anderer Namespace könnte zum Beispiel Prozessnamen sein:
#! /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]"
Zu verwenden als:
that-script motion 10 androidpush -msg 'There was a motion in your flat!'
(vorausgesetzt, ein sleep
Implementierung, die sich nicht um ihr argv[0]
kümmert ).
Wir verwenden bash
statt sh
für seine exec -a arg0
. Wenn Sie bash
nicht haben , andere Shells, die das unterstützen, enthalten ksh93
, mksh
, yash
und zsh
. Oder Sie könnten zu perl
zurückkehren nochmal.
Beachten Sie, dass dieser Namespace nicht reserviert ist. Nichts hindert einen anderen Benutzer daran, einen Prozess mit demselben Namen zu erstellen (im Gegensatz zur Verwendung eines ~/.lastrun
Datei im dateibasierten Ansatz), jedoch unter der Voraussetzung, dass diese Skripte hier alle durch dieselbe motion
gestartet werden Prozesses, könnten Sie die Suche nach Prozessen auf diejenigen mit derselben übergeordneten Prozess-ID beschränken:
Verbesserung, wenn alle Skripte immer von demselben Prozess gestartet werden
#! /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]"
Nur Linux:Verwenden Sie einen Kernelschlüssel mit einem Timeout
Unter Linux können Sie auch einen Schlüssel am Sitzungsschlüsselbund des Benutzers verwenden. Das ist nicht wirklich dafür ausgelegt, aber hier ist es praktisch, da diese Schlüssel über die Zeit bestehen bleiben und mit einem Timeout versehen werden können:
#! /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]"
Nun, es spielt in Ihrem Fall keine Rolle, da Sie nicht zwei Instanzen dieses Skripts gleichzeitig ausführen, aber alle haben eine Race-Bedingung, die es nicht garantiert, dass zwei Instanzen nicht ausgeführt werden innerhalb der angegebenen Zeit laufen. Wenn zwei Instanzen gleichzeitig ausgeführt werden, können beide überprüfen, ob die Bedingung in Ordnung ist, bevor eine von ihnen sie zurücksetzt.
Sperren einer Datei, um die Race-Condition zu vermeiden
Ein Ansatz, der das umgehen würde, wäre das sleep
Prozess hält eine Sperre für eine Datei:
#! /bin/sh -
file=${1?} time=${2?}
shift 2
{
flock -n 3 || exit 3
sleep "$time" 3>&3 &
exec "[email protected]"
} 3<> "$file"
(Außerdem hier sleep
und der ausgeführte Befehl würde die Sperre halten, die sicherstellen würde, dass nicht zwei Instanzen des Befehls gleichzeitig ausgeführt werden, selbst wenn ihre Ausführung länger dauert als der sleep
Befehl).