(只是为了完成:故事;如果您不想知道我为什么问这个问题,则无需阅读本文:我有一个带有摄像头运动检测功能的网络摄像头恶魔应用程序(称为“运动”)。当相机检测到运动时,该恶魔能够触发自定义命令。我还安装了一个小型命令行程序,可以轻松地将推送通知发送到我的 Android 手机。我将 cam-demon 配置为在检测到运动时运行该推送消息命令。)
问题:恶魔在他为运动拍摄的每一帧中都会触发该自定义命令,当连续运动仅 - 比方说 - 5 秒(相机帧速率设置为 10)时,我会收到大量推送通知图片/秒,所以我每秒收到 10 条推送消息,这被视为一个动作)。
我需要一个脚本或程序来阻止该 push-msg 命令(如果它已经在过去 x 秒/分钟内运行过)。
我可以想象这样的事情:
$ proxy_command.sh --block_time=10s --command==androidpush -msg "There was a motion in your flat!"
我找不到一种简单/优雅的方法来解决这个问题(无需编写带有时间戳的文件,然后检查该文件内容)。我也找不到有类似问题的人。
是否有某种命令代理或其他东西可以尽可能简单地解决我上面描述的问题?
答案1
将上次调用时间存储为文件的上次修改时间
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
并且如果该文件的寿命小于指定时间,则不会运行该命令。
壳
使用 BSD 或 GNU find
,您可以这样做:
#! /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
for it exec -a arg0
。如果您没有,其他支持的bash
shell 包括ksh93
、mksh
和。或者你可以再次恢复。yash
zsh
perl
请注意,该命名空间不是保留的。没有什么可以阻止其他用户创建具有相同名称的进程(与~/.lastrun
基于文件的方法中使用文件相反),但是考虑到这些脚本都是由同一motion
进程启动的,您可以将进程的搜索限制为具有相同父进程 ID 的进程:
改进了所有脚本始终由同一进程启动的情况
#! /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
)。