我有一项服务,它通过rsync
.当发生这种情况时,我想触发服务器端程序的执行。
由于该inotifywait
命令,监视文件或目录的更改相当容易。但我希望得到通知每次修改突发仅一次,因为上传后过程很繁重,并且不想为每个修改的文件执行它。
根据事件时间戳提出一些破解应该不是一个巨大的努力……但我相信这是一个相当常见的问题。但我没能找到任何有用的东西。
有什么巧妙的命令可以解决突发问题吗?我正在考虑可以这样使用的东西:
inotifywait -m "$dir" $opts | detect_burst --execute "$post_upload"
答案1
根据您自己的答案,如果您想使用 shell,read
您可以利用-t
超时选项,如果超时,它将返回代码设置为 >128。例如,您的burst
脚本可以松散地变为:
interval=$1; shift
while :
do if read -t $interval
then echo "$REPLY" # not timeout
else [ $? -lt 128 ] && exit # eof
"$@"
read || exit # blocking read infinite timeout
echo "$REPLY"
fi
done
您可能希望从初始阻塞读取开始,以避免在开始时检测到突发结束。
答案2
欧普在这里。
我提出的解决方案:它是一个名为burst
:
inotifywait -e moved_to "$monitored_dir" -m \
| burst 2 'echo run post-upload'
剧本burst
:
#!/bin/bash
help() {
>&2 echo "Usage: $0 <interval> <command>"
}
set -e
trap help EXIT
interval=${1:?missing interval}; shift
: ${1:?missing command}
trap - EXIT
set +e
exec 3> >(sed "s/^/burst: /" >&2)
while read line; do
echo "$line" >&3
test -n "$!" && kill -term $! 2>/dev/null
(sleep $interval && $SHELL -c "$*") &
done
基本上,这是创建一个子 shell,它在启动实际命令( )之前等待定义的时间(sleep $interval && $SHELL -c "$*")
。读取的任何新行inotifywait
都会简单地杀死此类 shell(如果存在)并再次创建它。
一旦连串的行结束,shell 将能够结束sleep
,并且命令将被执行。
它有一些缺点:
它会为 stdin 中的每一行杀死并生成一个进程。这意味着您希望首先减少行数(因此
-e moved_to
需要过滤器inotifywait
)。如果上传的文件数量足够大,它无法适应 wrt 性能!如果在几秒钟后出现第二次突发
$interval
(或者文件传输需要这段时间),它很可能会终止正在运行的更新后进程。只要这样的过程是幂等的或事务性的,那就没问题……所以请注意。
答案3
楼主。一个可能的解决方案可能类似于
inotifywait -m "$dir" -e moved_to --timefmt='%s' --format '%T' | stdbuf -oL uniq | ...
编辑: 请参阅我对此问题的其他答案,因为恕我直言,第二个答案更好
它的工作原理是在每次执行时打印纪元以来的时间move_to
,这是单文件传输的最后一步。
即使一次上传持续超过一秒,它可能会多次触发后上传过程,但这种方法工作得很好。
--timefmt
通过将标志更改为某个不同的值可以获得不同的粒度。
将其发布在这里作为半不错的想法,尽管我不太喜欢它......但我仍然想分享它。
答案4
您可以创建一个循环来检查当前时间并跳过与先前事件相同(或更一般地说,以您可以定义的方式类似)的事件。在 bash、ksh 或 zsh 中,以下简单循环会跳过 10 秒周期内的重复事件:
inotifywait -m … | {
previous_SECONDS=0
previous_event=
while IFS= read -r event; do
if [[ "$event" = "$previous_event" && $SECONDS <= $previous_SECONDS + 10 ]]; then
continue
fi
previous_SECONDS=$SECONDS previous_event=$event
"$post_upload" "$event"
done
}
Shell 不擅长高性能文本处理,因此最好使用不同的工具。您可以使用 awk,由于历史原因,它有一种非常奇怪的方式来公开时间(自纪元以来的秒数):调用srand()
返回上次调用srand()
.
inotifywait -m … | awk '
{srand(); current_time = srand()}
$0 == previous_event && current_time <= previous_time + 10 {
system(ENVIRON["post_upload"])
}
{previous_event = $0; previous_time = current_time}
'
除非您确实需要可移植性,否则我会放弃它并使用更高级的脚本语言,例如 Perl、Python 或 Ruby。这些语言都有一个 inotify 接口,因此不需要调用inotifywait
并进行任何文本解析。