(Solo per completare:la storia; non c'è bisogno di leggere questo se non vuoi sapere perché sto facendo questa domanda :Ho ricevuto un'applicazione demone della webcam (chiamata "movimento") con rilevamento del movimento della fotocamera. Questo demone è in grado di attivare un comando personalizzato quando viene rilevato un movimento dalla telecamera. Ho inoltre installato un piccolo programma da riga di comando che può inviare facilmente notifiche push al mio cellulare Android. Ho configurato la cam-demon per eseguire quel comando push-message mentre rileva un movimento.)
Il problema:il demone attiva quel comando personalizzato con ogni fotogramma che prende per un movimento, portandomi a un'enorme quantità di notifiche push quando c'è un movimento continuo per soli – diciamo – 5 secondi (il framerate della fotocamera è impostato su 10 pics/secondo quindi ottengo 10 push-msg al secondo che è considerato un movimento).
Ho bisogno di uno script o di un programma che blocchi quel comando push-msg se è già stato eseguito negli ultimi x secondi/minuti.
Potrei immaginare qualcosa come:
$ proxy_command.sh --block_time=10s --command==androidpush -msg "There was a motion in your flat!"
Non sono riuscito a trovare un modo semplice/ma elegante per risolvere questo problema (senza scrivere file con timestamp e quindi controllare il contenuto di quel file). Né sono riuscito a trovare qualcuno con un problema simile.
Esiste una sorta di proxy di comando o qualcosa che possa risolvere il mio problema come descritto sopra nel modo più semplice possibile?
Risposta accettata:
Memorizzazione dell'ora dell'ultima chiamata come ora dell'ultima modifica di un file
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;
Da usare come:
that-script /some/file 10 androidpush -msg 'There was a motion in your flat!'
Registra l'ora dell'ultima esecuzione come ora dell'ultima modifica di /some/file
e non esegue il comando se l'età di quel file è inferiore al tempo specificato.
conchiglia
Con BSD o GNU find
, potresti farlo con:
#! /bin/sh -
file=${1?} time=${2?}
shift 2
[ "$(find -- "$file" -prune -newermt "$time ago")" ] && exit 3
touch -- "$file"
exec "[email protected]"
Per eseguire come:
that-script /some/file 10sec androidpush -msg 'There was a motion in your flat!'
In ogni caso, dovrai archiviare le informazioni sull'ultima corsa in un posto che persiste per le corse successive. Il file system è un posto ovvio per questo. Questo è uno in cui puoi riservare un'area per te stesso.
processo di sospensione con nome specifico
Un altro spazio dei nomi potrebbe essere, ad esempio, i nomi dei processi:
#! /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]"
Da utilizzare come:
that-script motion 10 androidpush -msg 'There was a motion in your flat!'
(supponendo un sleep
implementazione che non si preoccupa del suo argv[0]
).
Stiamo usando bash
invece di sh
per il suo exec -a arg0
. Se non hai bash
, altre shell che supportano che includono ksh93
, mksh
, yash
e zsh
. Oppure puoi tornare a perl
di nuovo.
Nota che quello spazio dei nomi non è riservato. Non c'è niente che impedisca a un altro utente di creare un processo con lo stesso nome (invece di usare un ~/.lastrun
file nell'approccio basato su file), tuttavia dato che qui quegli script sono tutti avviati dallo stesso motion
processo, puoi limitare la ricerca del processo a quelli con lo stesso ID processo padre:
Miglioramento per quando tutti gli script vengono avviati sempre con lo stesso processo
#! /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]"
Solo Linux:usa una chiave del kernel con un timeout
Su Linux, potresti anche usare una chiave sul portachiavi di sessione dell'utente. Non è stato progettato proprio per quello, ma qui è utile poiché quelle chiavi persistono nel tempo e possono essere concesse un timeout:
#! /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]"
Ora, nel tuo caso non importa perché non stai eseguendo due istanze di quello script contemporaneamente, ma tutti hanno condizioni di gara che non garantiscono che due istanze non saranno eseguire entro il tempo specificato. Se vengono eseguite due istanze contemporaneamente, entrambe potrebbero verificare che la condizione sia OK prima che una di esse la reimposti.
Blocco di un file per evitare la race condition
Un approccio che potrebbe aggirare il problema sarebbe avere il sleep
processo di blocco di un file:
#! /bin/sh -
file=${1?} time=${2?}
shift 2
{
flock -n 3 || exit 3
sleep "$time" 3>&3 &
exec "[email protected]"
} 3<> "$file"
(in aggiunta qui, entrambi sleep
e il comando in esecuzione manterrebbe il blocco che assicurerebbe che due istanze del comando non vengano eseguite contemporaneamente anche se impiegano più tempo per essere eseguite rispetto al sleep
comando).