#!/usr/bin/env dash # compat: +ash +bash +dash -hush -ksh +mksh +oksh +osh +posh +yash +zsh minutemaid() { ### @- ### check if the current minute is divisible by a given number, ### and optionally execute a command if it is. if a command is given, ### either execute the command and return its exit value, ### or execute nothing and return 0. if a command is omitted, ### either return 0 on the minute, or return 1. ### ### note that a "minute" is relative to the seconds since the epoch, ### not the minute of the hour. this ensures that commands will run ### roughly every N minutes, regardless of the minute hand on the clock. ### ### note that `minutemaid 1` will always return 0, ### and `minutemaid 1 command` will always execute the command, ### since every integral interval is evenly divisible by 1. ### `minutemaid 0`, and any negative interval, is an error. ### ### ``` ### # crontab usage: ### * * * * * minutemaid 9 ~/work/do_my_bidding # runs every nine minutes ### # loop usage: ### until minutemaid 9; do sleep 5; done ### echo the wait is finally over; date ### # improper usage: ### while minutemaid 1; do sleep 1; done ### echo this will never happen ### ``` local offset=0 name= while getopts 'o:h' name; do case $name in o) offset="$OPTARG";; ?) local fd=0 # FIXME: this also matches single-character filenames! [ $name = h ] && fd=1 || fd=2 printf '%s\n' "usage: $0 [-o offset] {interval} [{command} [{args...}]]" >&$fd [ $name = h ] && return 0 || return 1;; esac done shift $((OPTIND-1)) local interval="${1:?no interval specified}" shift if [ "$interval" -gt 0 ] 2>/dev/null; then : else printf "%s\n" "$0: interval must be a positive integer" >&2 return 1 fi local sec="$(date +%s)" local min="$((sec/60+offset))" local mod="$((min%interval))" if [ $# -gt 0 ]; then [ $mod -ne 0 ] || "$@" else [ $mod -eq 0 ] && return 0 || return 1 fi } [ -n "${preload+-}" ] || minutemaid "$@"