#!/usr/bin/env bash # Minimal throttle runner: queues a command once within a delay window. set -o pipefail name="throt" config_file="${HOME}/.throt" [[ -r "$config_file" ]] && . "$config_file" lock_dir="${THROT_LOCK_DIR:-$HOME/tmp/throt-locks}" log_file="${THROT_LOG_FILE:-$HOME/tmp/throt.log}" usage() { cat < EOF exit 1 } log() { local ts ts=$(date -Iseconds) printf '%s %s\n' "$ts" "$*" >>"$log_file" } ensure_dirs() { mkdir -p "$lock_dir" mkdir -p "$(dirname "$log_file")" } file_age() { local path="$1" now="$2" ts if read -r ts <"$path" 2>/dev/null && [[ "$ts" =~ ^[0-9]+$ ]]; then echo $((now - ts)) return fi ts=$(stat -f %m "$path" 2>/dev/null || stat -c %Y "$path" 2>/dev/null || echo "$now") echo $((now - ts)) } parse_args() { delay="" detached=0 while [[ $# -gt 0 ]]; do case "$1" in --delay) delay="${2:-}" shift 2 || usage ;; --detached) detached=1 shift ;; --) shift break ;; -*) usage ;; *) break ;; esac done [[ -n "$delay" && "$delay" =~ ^[0-9]+$ && "$delay" -gt 0 ]] || usage cmd=("$@") [[ ${#cmd[@]} -gt 0 ]] || usage } start_detached() { local delay="$1" lock_file="$2" shift 2 nohup "$0" --detached --delay "$delay" -- "$@" >/dev/null 2>&1 & log "[main] queued delay=$delay lock=$lock_file cmd=${*}" } main_run() { parse_args "$@" ensure_dirs local cmd_str="${cmd[*]}" local hash hash=$(printf '%s' "$cmd_str" | shasum -a 256 | awk '{print $1}') local lock_file="${lock_dir}/${hash}.lock" local now now=$(date +%s) local window=$((delay * 5)) if [[ -f "$lock_file" ]]; then local age age=$(file_age "$lock_file" "$now") if (( age < window )); then log "[main] skip existing age=${age}s delay=$delay lock=$lock_file cmd=${cmd_str}" exit 0 fi rm -f "$lock_file" log "[main] removed stale age=${age}s delay=$delay lock=$lock_file" fi if ( set -o noclobber; : >"$lock_file" ) 2>/dev/null; then printf '%s\n' "$now" >"$lock_file" start_detached "$delay" "$lock_file" "${cmd[@]}" else log "[main] skip lock-race lock=$lock_file cmd=${cmd_str}" fi } worker_run() { parse_args "$@" ensure_dirs local cmd_str="${cmd[*]}" local hash hash=$(printf '%s' "$cmd_str" | shasum -a 256 | awk '{print $1}') local lock_file="${lock_dir}/${hash}.lock" trap 'rm -f "$lock_file"' EXIT sleep "$delay" log "[worker] start delay=$delay lock=$lock_file cmd=${cmd_str}" "${cmd[@]}" local status=$? log "[worker] done status=$status cmd=${cmd_str}" exit "$status" } if [[ "${1:-}" == "--detached" ]]; then worker_run "$@" else main_run "$@" fi