我有兴趣包装一个命令,使其每 X 持续时间最多运行一次;本质上,与 lodash 具有相同的功能throttle
功能。我基本上希望能够运行这个:
throttle 60 -- check-something
another-command
throttle 60 -- check-something
another-command
throttle 60 -- check-something
对于其中每个命令,如果自(成功)运行throttle
以来不到 60 秒,则将跳过该命令。check-something
这样的东西已经存在了吗?用shell脚本做起来容易吗?
答案1
我不知道有什么现成的东西,但包装函数可以完成这项工作。我在 bash 中使用关联数组实现了一个:
declare -A _throttled=()
throttle() {
if [ "$#" -lt 2 ]
then
printf '%s\n' "Usage: throttle timeout command [arg ... ]" >&2
return 1
fi
local t=$1
shift
if [ -n "${_throttled["$1"]}" ]
then
if [ "$(date +%s)" -ge "${_throttled["$1"]}" ]
then
"$@" && _throttled["$1"]=$((t + $(date +%s)))
else
: printf '%s\n' "Timeout for: $1 has not yet been reached" >&2
fi
else
"$@" && _throttled["$1"]=$((t + $(date +%s)))
fi
}
基本逻辑是:如果该命令在数组中有条目_throttle
,则根据数组值检查当前时间;如果超时已过期,请运行该命令并(如果该命令成功)设置新的超时值。如果超时尚未到期,(不要)打印信息性消息。另一方面,如果该命令在数组中还没有条目,则运行该命令并(如果该命令成功)设置一个新的超时值。
包装函数不根据任何参数区分命令,因此throttle 30 ls
与throttle 30 ls /tmp
.通过替换"$1"
to的数组引用和赋值可以轻松更改这一点"$@"
。
--
另请注意,我从您的示例语法中删除了。
另请注意,这仅限于秒级分辨率。
如果您有 bash 版本 4.2 或更高版本,您可以使用内置date
功能来保存对外部命令的调用:printf
...
_throttled["$1"]=$((t + $(printf '%(%s)T\n' -1)))
...
...我们要求当前时间(%s)
明确以秒为单位的时间 ( -1
)。
或者在bash
5.0 或更高版本中:
_throttled["$1"]=$((t + EPOCHSECONDS))
答案2
和zsh
:
typeset -A last_run
zmodload zsh/datetime
throttle() {
local delay=$1; shift
# $cmd is the (minimally) quoted arguments of the command joined
# with spaces and used as the key for the `$last_run` associative array
local cmd="${(j: :)${(q+)@}}"
local now=$EPOCHREALTIME
local lr=$last_run[$cmd]
local t=$((now - lr))
if ((t < delay)); then
printf >&2 '%s was already run successfully %.3g seconds ago\n' "$cmd" "$t"
return 1
else
"$@" && last_run[$cmd]=$now
# $now being the time when the command started, replace with
# $EPOCHREALTIME if you want the time it finished.
fi
}
throttle 3.5 echo "test 1 2 3"
sleep 2
throttle 3.5 echo "test 1 2 3"
sleep 4
throttle 3.5 echo "test 1 2 3"
假设给定命令的所有实例都throttle
在同一个 shell 进程中运行(而不是在子 shell 中)。