実行が制限されるようにコマンドをラップする方法(例:X分ごとに最大1回実行)

実行が制限されるようにコマンドをラップする方法(例:X分ごとに最大1回実行)

基本的には、lodashと同じ機能で、X期間ごとに最大1回実行されるようにコマンドをラップすることに興味があります。throttle機能。私は基本的にこれを実行できるようにしたいです。

throttle 60 -- check-something
another-command
throttle 60 -- check-something
another-command
throttle 60 -- check-something

これらの各コマンドに対してthrottle(成功的に)実行されてから60秒未満が経過すると、コマンドはスキップされます。check-somethingこのようなものはすでに存在していますか?シェルスクリプトで簡単にできますか?

答え1

私は基本的に何も知りませんが、ラッパー機能はこれを行います。連想配列を使用してbashで1つを実装しました。

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"簡単に変更できます"$@"

--また、サンプル構文からこれを削除しました。

さらに、これは第2の解像度に限定される。

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与えられたコマンドのすべてのインスタンスが同じシェルプロセス内で(サブシェルではなく)実行されるとします。

関連情報