(Просто для завершения: история, вам не нужно читать это, если вы не хотите знать, почему я задаю этот вопрос : у меня есть демон-приложение веб-камеры (называемое «движение») с обнаружением движения камеры. может вызвать пользовательскую команду при обнаружении движения камерой. Кроме того, я установил крошечную программу командной строки, которая легко может отправлять 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 вы также можете использовать ключ в сеансе сеанса пользователя. Это на самом деле не предназначено для этого, но здесь это удобно, поскольку эти ключи сохраняются во времени и могут быть предоставлены таймаутом:
#! /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
).