in a world where no one cares

Stupid simple command line wall timer

I was making dinner and needed to time something. My phone already had another timer going, and running two timers simultaneously is clearly beyond the capacity of a modern smartphone, so I reached for my laptop instead. And as I Googled “macos desktop timer” (because having any timer at all built into macOS is clearly an extravagance, and not one Apple has seen fit to bequeath us with), my Wi-Fi dropped. So I grabbed a terminal, and ran:

$ sleep $((15 * 60)) && say "Onion time"

(For those unfamiliar with macOS, the say utility is a simple frontend for the built-in text-to-speech engine. On Linux, substitute something like ffplay ding.wav – I like a microwave bell.)

This seemed convenient, so after the culinary crisis had passed, I wrapped the snippet in a function and added it to my .profile.

But then I thought it would be nice to have a real-time progress indicator. I thought about writing my own with a loop, a one-second sleep, and a printf involving a carriage return to repeatedly re-render a counter on the same line. Something like:

remaining="$duration"
while [ $remaining -gt 0 ]
do
    printf "\r% 10d" "$remaining"
    remaining=$(($remaining - 1))
    sleep 1
done
echo

But that leads to wanting to display both elapsed and remaining times, and maybe ETA, and really, a nice progress bar is what I really want. So, what do I already have installed which can give me a progress bar? Well, pv(1) is pretty great. So all I need to do is feed it a byte every second for as long as I want my timer to run. Except, not quite, because I remembered that pv can enforce rudimentary rate-limiting. So maybe I just need to feed it as many bytes as the number of seconds I want the timer to run for, and limit the throughput to 1 B/s. Then select a set of display options which are only concerned with time (not sizes or throughput).

All said and done, I ended up with:

alarm ()
{
    if [ $# -lt 1 ]
    then
        echo "Usage: alarm duration [message]" 1>&2
        return 1
    fi

    duration="$1"
    shift
    message="$@"
    if [ -z "$message" ]
    then
        message="This is your $duration second alarm."
    fi

    dd if=/dev/zero bs="$duration" count=1 2>/dev/null \
        | pv --progress --timer --eta --size "$duration" --rate-limit 1 >/dev/null \
        && say --voice Samantha "$message"
}

In action, it looks like this:

0:00:03 [=========>                        ] 30% ETA 0:00:07

And I’m sure I’ll continue to be pretty happy with it until I get something very wet or very powdery all over my laptop.

Write a Comment

Comment