(Solo para completar:la historia; no es necesario que lea esto si no quiere saber por qué hago esta pregunta :Tengo una aplicación demoníaca de cámara web (llamada "movimiento") con detección de movimiento de cámara. Este demonio puede activar un comando personalizado cuando la cámara detecta un movimiento. Además, instalé un pequeño programa de línea de comandos que puede enviar fácilmente notificaciones automáticas a mi móvil Android. Configuré el demonio de la cámara para ejecutar ese comando de mensaje de inserción cuando detecta un movimiento).
El problema:el demonio activa ese comando personalizado con cada fotograma que toma para un movimiento, lo que me lleva a una gran cantidad de notificaciones automáticas cuando hay un movimiento continuo durante solo, digamos, 5 segundos (la velocidad de fotogramas de la cámara está configurada en 10 fotos/segundo, así que recibo 10 mensajes push cada segundo que se considera un movimiento).
Necesito un script o programa que bloquee ese comando push-msg si ya se ha ejecutado en los últimos x segundos/minutos.
Podría imaginar algo como:
$ proxy_command.sh --block_time=10s --command==androidpush -msg "There was a motion in your flat!"
No pude encontrar una manera fácil pero elegante de resolver esto (sin escribir archivos con marcas de tiempo y luego verificar el contenido de esos archivos). Tampoco pude encontrar a alguien que tuviera un problema similar.
¿Hay algún tipo de proxy de comando o algo que pueda resolver mi problema como el descrito anteriormente de la manera más fácil posible?
Respuesta aceptada:
Almacenamiento de la hora de la última invocación como hora de la última modificación de un archivo
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;
Para ser usado como:
that-script /some/file 10 androidpush -msg 'There was a motion in your flat!'
Registra la hora de la última ejecución como la hora de la última modificación de /some/file
y no ejecuta el comando si la antigüedad de ese archivo es inferior al tiempo especificado.
cáscara
Con BSD o GNU find
, podrías hacerlo con:
#! /bin/sh -
file=${1?} time=${2?}
shift 2
[ "$(find -- "$file" -prune -newermt "$time ago")" ] && exit 3
touch -- "$file"
exec "[email protected]"
Para ejecutar como:
that-script /some/file 10sec androidpush -msg 'There was a motion in your flat!'
En cualquier caso, deberá almacenar la información sobre la última ejecución en algún lugar que persista para las próximas ejecuciones. El sistema de archivos es un lugar obvio para eso. Ese es uno en el que puedes reservar un área para ti.
proceso de suspensión con nombre específico
Otro espacio de nombres podría ser, por ejemplo, nombres de procesos:
#! /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]"
Para ser utilizado como:
that-script motion 10 androidpush -msg 'There was a motion in your flat!'
(asumiendo un sleep
implementación a la que no le importa su argv[0]
).
Estamos usando bash
en lugar de sh
para su exec -a arg0
. Si no tienes bash
, otros shells compatibles incluyen ksh93
, mksh
, yash
y zsh
. O podría volver a perl
de nuevo.
Tenga en cuenta que ese espacio de nombres no está reservado. No hay nada que impida que otro usuario cree un proceso con el mismo nombre (a diferencia de usar un ~/.lastrun
file en el enfoque basado en archivos), sin embargo, dado que aquí todos esos scripts se inician con el mismo motion
proceso, puede restringir la búsqueda de proceso a aquellos con la misma identificación de proceso principal:
Mejora para cuando todos los scripts siempre se inician con el mismo proceso
#! /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:use una clave de kernel con un tiempo de espera
En Linux, también podría usar una clave en el conjunto de claves de la sesión del usuario. Realmente no se ha diseñado para eso, pero aquí es útil ya que esas claves persisten a lo largo del tiempo y se les puede dar un tiempo de espera:
#! /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]"
Ahora, realmente no importa en su caso, ya que no está ejecutando dos instancias de ese script al mismo tiempo, pero todos tienen condiciones de carrera que hacen que no garantice que dos instancias no serán ejecutar dentro del tiempo especificado. Si se ejecutan dos instancias al mismo tiempo, ambas podrían verificar que la condición es correcta antes de que cualquiera de ellas la restablezca.
Bloquear un archivo para evitar la condición de carrera
Un enfoque que funcionaría sería tener el sleep
proceso mantener un bloqueo en un archivo:
#! /bin/sh -
file=${1?} time=${2?}
shift 2
{
flock -n 3 || exit 3
sleep "$time" 3>&3 &
exec "[email protected]"
} 3<> "$file"
(además aquí, ambos sleep
y el comando que se está ejecutando mantendría el bloqueo, lo que garantizaría que no se ejecuten dos instancias del comando al mismo tiempo, incluso si tardan más en ejecutarse que sleep
comando).