Armazenando o último horário de chamada como a hora da última modificação de um arquivo
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!'
Ele registra a hora da última execução como a hora da última modificação de /some/file
e não executa o comando se a idade desse arquivo for menor que a hora especificada.
shell
Com o BSD ou o GNU find
, você pode fazer isso com:
#! /bin/sh -
file=${1?} time=${2?}
shift 2
[ "$(find -- "$file" -prune -newermt "$time ago")" ] && exit 3
touch -- "$file"
exec "$@"
Para executar como:
that-script /some/file 10sec androidpush -msg 'There was a motion in your flat!'
Em qualquer caso, você terá que armazenar as informações sobre a última execução em algum lugar que persista para as próximas execuções. O sistema de arquivos é um lugar óbvio para isso. Essa é aquela em que você pode reservar alguma área para si mesmo.
processo de sono com nome específico
Outro namespace poderia ser, por exemplo, nomes de processos:
#! /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 "$@"
Para ser usado como:
that-script motion 10 androidpush -msg 'There was a motion in your flat!'
(assumindo uma implementação de sleep
que não se importa com o seu argv[0]
).
Estamos usando bash
em vez de sh
para seu exec -a arg0
. Se você não tiver bash
, outros shells compatíveis são ksh93
, mksh
, yash
e zsh
. Ou você pode reverter para perl
novamente.
Observe que esse namespace não está reservado. Não há nada que impeça outro usuário de criar um processo com o mesmo nome (em vez de usar um arquivo ~/.lastrun
na abordagem baseada em arquivo), no entanto, considerando que esses scripts são iniciados pelo mesmo processo motion
, você poderia restringir a pesquisa por processo àqueles com o mesmo ID de processo pai:
Melhoria para quando todos os scripts são sempre iniciados pelo mesmo 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 "$@"
Somente Linux: use uma chave do kernel com um tempo limite
No Linux, você também pode usar uma chave no chaveiro da sessão do usuário. Isso realmente não foi projetado para isso, mas aqui é útil, pois essas chaves persistem ao longo do tempo e podem receber um tempo limite:
#! /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 "$@"
Agora, isso não importa realmente no seu caso, pois você não está executando duas instâncias desse script ao mesmo tempo, mas todas elas têm condições de corrida, o que faz com que ele não garanta que duas instâncias ganhas pode ser executado dentro do tempo especificado. Se duas instâncias forem executadas ao mesmo tempo, elas podem verificar se a condição está correta antes de qualquer uma delas reconfigurá-la.
Bloquear um arquivo para evitar a condição de corrida
Uma abordagem que funcionaria em torno disso seria ter o processo sleep
mantendo um bloqueio em um arquivo:
#! /bin/sh -
file=${1?} time=${2?}
shift 2
{
flock -n 3 || exit 3
sleep "$time" 3>&3 &
exec "$@"
} 3<> "$file"
(além disso, ambos sleep
e o comando sendo executado manterão o bloqueio, o que garantirá que duas instâncias do comando não sejam executadas ao mesmo tempo, mesmo que demorem mais tempo para serem executadas do que o comando sleep
) .