Блокировать / предотвращать команду, если она была выполнена в течение последних x секунд / минут

(Просто для завершения: история, вам не нужно читать это, если вы не хотите знать, почему я задаю этот вопрос : у меня есть демон-приложение веб-камеры (называемое «движение») с обнаружением движения камеры. может вызвать пользовательскую команду при обнаружении движения камерой. Кроме того, я установил крошечную программу командной строки, которая легко может отправлять push-уведомления моему мобильному телефону Android. Я настроил cam-demon для запуска этой команды push-message как он обнаруживает движение.)

Проблема: демон запускает эту настраиваемую команду с каждым фреймом, который он берет для движения, что приводит меня к огромному количеству push-уведомлений, когда есть непрерывное движение для просто – скажем, – 5 секунд (частота кадров камеры установлена ​​на 10 pics / second, поэтому я получаю 10 push-msgs каждую секунду, что считается движением).

Мне нужен скрипт или программа, которые блокируют эту команду push-msg, если она была запущена в прошлом x секунд / минут.

Я мог представить себе что-то вроде:

$ 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 и не запускает команду, если возраст этого файла меньше указанного времени.

оболочка

С помощью find BSD или GNU вы можете сделать это с помощью:

 #! /bin/sh - file=${1?} time=${2?} shift 2 [ "$(find -- "$file" -prune -newermt "$time ago")" ] && exit 3 touch -- "$file" exec "$@" 

Для запуска:

 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 "$@" 

Используется как:

 that-script motion 10 androidpush -msg 'There was a motion in your flat!' 

(предполагая реализацию sleep , которая не заботится о ее argv[0] ).

Мы используем bash вместо sh для своего exec -a arg0 . Если у вас нет bash , другие оболочки, которые поддерживают, включают ksh93 , mksh , mksh и zsh . Или вы можете снова вернуться к perl .

Обратите внимание, что это пространство имен не зарезервировано. Нет ничего, что мешало бы другому пользователю создавать процесс с таким же именем (в отличие от использования файла ~/.lastrun в файловом подходе), однако при условии, что здесь все эти сценарии запускаются одним и тем же motion , вы можете ограничить поиск процесса для тех, у кого одинаковый идентификатор родительского процесса:

Улучшение, когда все сценарии всегда запускаются одним и тем же процессом

 #! /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 "$@" 

Только для 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 "$@" 

Теперь это не имеет особого значения в вашем случае, так как вы не запускаете два экземпляра этого сценария одновременно, но у всех из них есть условия гонки, из-за чего это не гарантирует, что два экземпляра не будут выполняться в течение указанного времени. Если два экземпляра запускаются одновременно, они могут проверить, что условие в порядке, прежде чем какой-либо из них сбрасывает его.

Блокировка файла во избежание состояния гонки

Подход, который будет работать вокруг, будет заключаться в том, чтобы процесс sleep сохранял блокировку файла:

 #! /bin/sh - file=${1?} time=${2?} shift 2 { flock -n 3 || exit 3 sleep "$time" 3>&3 & exec "$@" } 3<> "$file" 

(кроме того, как sleep и выполняемая команда будут содержать блокировку, которая обеспечит одновременное выполнение двух экземпляров команды, даже если они занимают больше времени, чем команда sleep ).