Skip to content

Repository files navigation

sleepgo

A drop-in replacement for sleep that can show you how much of the wait is left.

a progress bar counting down a ten second sleep

sleepgo -p 30      # wait 30 seconds, with a progress bar
sleepgo -c 5m      # wait 5 minutes, with a countdown
sleepgo 1m 30s     # wait 90 seconds silently, exactly like sleep

Single binary, no runtime dependencies, nothing outside the Go standard library.

Why

sleep has two flags: --help and --version. There is no way to ask it for a countdown, a progress bar, or any sign of life at all. Everything people call a flag on sleep is really part of the duration operand (sleep 1m 30s).

So the wait for a long backup, a rate limit window, or a deploy cooldown happens behind a blank line, and you end up writing this again:

for i in $(seq 30 -1 1); do
    printf '\rwaiting: %2ds ' "$i"
    sleep 1
done
echo

sleepgo is that, as a flag. Called without flags it behaves like sleep, so you can put it in the same places.

Install

With Go 1.21 or newer:

go install github.com/didvc/sleepgo@latest

The binary lands in $(go env GOPATH)/bin, usually ~/go/bin. Add that to your PATH if it is not there already.

From source:

git clone https://github.com/didvc/sleepgo
cd sleepgo
go build -o sleepgo .

Or grab a prebuilt binary from the releases page.

Quick start

sleepgo 5                  # five seconds, silent
sleepgo -p 5               # five seconds, progress bar
sleepgo -c 5               # five seconds, countdown only
sleepgo -v 5               # print the wake-up time, then wait
sleepgo -p 2h              # two hours
sleepgo -p 1h 30m          # ninety minutes; operands add up
sleepgo -p inf             # wait until interrupted

Options

Flag What it does
-p, --progress Draw a progress bar.
-c, --countdown Show the remaining time only.
-v, --verbose Print the wake-up time before waiting and the elapsed time after.
-q, --quiet Silence everything, including the flags above.
-w N, --width N Assume the terminal is N columns wide.
--style NAME Bar glyphs: unicode (default) or ascii.
--no-color Turn off colour.
-h, --help Print usage.
-V, --version Print the version.

Options can go before, after, or between the duration operands. Short options bundle, so -pv and -pw40 both work, and -- ends option parsing.

sleepgo -p 30              # same thing
sleepgo 30 -p              # ...
sleepgo 1m -p 30s          # ...

the help output

Durations

The operand format is the one sleep uses.

You write You get
30 30 seconds
1.5 1.5 seconds
90s 90 seconds
5m 5 minutes
2h 2 hours
1d 1 day
1h 30m 90 minutes
1m 30s 500ms ❌ rejected — ms is not a suffix
inf, infinity wait until interrupted

Suffixes are s (seconds, the default), m, h and d. Several operands in a row are added together. Fractions and exponents are fine: 0.25, .5 and 1234e-3 all mean the same wait as 1.234.

What it looks like

Progress bar

The bar fills as the wait goes on, with the total on the left and the time remaining on the right.

progress bar at forty percent

Countdown

-c drops the bar and keeps the number, which suits a long wait where you only glance at it occasionally.

countdown showing 57s left

ASCII

Terminals and fonts that mangle block glyphs get --style=ascii.

ascii progress bar

Narrow terminals

In a small split or a tmux pane, the display drops the parts that no longer fit, so it always stays on one line.

narrow terminal display

Verbose

-v tells you when the wait will be over before it starts, which is handy for anything longer than a coffee break.

verbose output

Recipes

Wait out a rate limit and see the time go down:

sleepgo -c 15m && curl -s https://api.example.com/v1/things

Put a visible pause between steps in a script, but only when someone is watching:

[ -t 2 ] && sleepgo -p 10 || sleepgo 10

Retry with a visible backoff:

for attempt in 1 2 3 4 5; do
    fetch_the_thing && break
    echo "attempt $attempt failed, backing off"
    sleepgo -c "$(( attempt * 10 ))"
done

Hold a container or a shell open until something kills it:

sleepgo inf

Give yourself a moment to cancel before something destructive runs:

echo "wiping $TARGET — Ctrl-C to stop"
sleepgo -p 10
rm -rf "$TARGET"

Using it in place of sleep

With no flags, sleepgo and sleep do the same thing, so an alias is enough:

alias sleep=sleepgo

To get the progress bar without typing -p every time, set an environment variable:

export SLEEPGO_PROGRESS=1

Scripts that want the old silent behaviour can still ask for it with -q, and SLEEPGO_PROGRESS=0 turns the default back off.

Since the display is written to stderr, redirecting or piping stdout leaves it alone:

sleepgo -p 5 > results.txt        # bar still visible
sleepgo -p 5 | while read -r l; do :; done

Interrupting and suspending

Ctrl-C stops the wait, clears the bar and puts your cursor back. The process ends the same way sleep does when interrupted, so a Ctrl-C inside a loop still breaks out of the loop.

interrupted with Ctrl-C

Ctrl-Z suspends it. The display gets out of the way first, and fg brings it back and carries on drawing. Time keeps passing while a process is stopped, so a long suspension can mean the wait is already over when you return, which is also what happens with sleep.

Exit status

Status Meaning
0 The wait finished.
1 Bad option or unparseable duration.
128+N Killed by signal N — 130 for Ctrl-C, 143 for SIGTERM.

Output and terminals

Everything the display writes goes to stderr, which keeps stdout clean for pipelines and still shows the bar when stdout is redirected somewhere else.

When stderr is a pipe, a file, or a terminal that cannot handle cursor movement, the display switches to one line at the start and one at the end. Log files stay readable that way.

output when piped

Colour follows the NO_COLOR convention, and --no-color turns it off for a single run. Terminal width comes from the terminal itself, falling back to $COLUMNS and then to 80 columns; -w overrides all of it.

Environment variables

Variable Effect
SLEEPGO_PROGRESS Set to 1 to make --progress the default.
NO_COLOR Any value disables colour.
COLUMNS Terminal width, used when the terminal does not report one.
TERM TERM=dumb selects the plain line output.

Compatibility with GNU sleep

Operand handling matches GNU coreutils, including the corners:

  • Fractions, exponents and hex all parse the way C's strtod reads them, so 0.25, 1234e-3 and 0x10 are accepted, and 0x1d is 29 seconds rather than one day.
  • nan, negative numbers and trailing junk are rejected with the same invalid time interval message.
  • -1 is reported as an unknown option, which is what GNU's option parser does with it.
  • inf and infinity wait forever.

Every operand in the table above was diffed against GNU coreutils 8.30, and the two agree on all of them.

One difference worth knowing: a job suspended with Ctrl-Z is reported by the shell as stopped by SIGSTOP (147) rather than SIGTSTP (148). Go programs cannot re-raise SIGTSTP and reliably stop, so sleepgo raises the signal that always works.

Accuracy

Waiting is anchored to a single deadline taken at the start, so drawing a progress bar does not make the wait longer. Timing for a one second request:

sleep 1           1004 ms
sleepgo 1         1005 ms
sleepgo -p 1      1005 ms   (twenty screen updates during the wait)

The deadline uses a monotonic clock. Clock changes during the wait — an NTP correction, a suspend and resume, someone running date — leave the length of the wait alone.

A silent sleepgo wakes up once. A countdown wakes once per second, timed to the moment the digit changes. A progress bar wakes roughly four times per bar cell, between 20 times a second and once a second, so a long wait stays cheap.

When to use something else

Most of the cost of sleep 0.01 in a tight shell loop is starting the process, and a Go binary starts more slowly than a C one:

/bin/true         0.41 ms per spawn
sleep 0           0.47 ms per spawn
sleepgo 0         0.90 ms per spawn

Half a millisecond is nothing for sleepgo 30 and it is the entire cost for sleepgo 0.001 in a loop. If you are spawning thousands of tiny sleeps, do the waiting inside one long-lived process instead. For everything else — the waits where a progress bar is worth having — it disappears into the noise.

Building and testing

go build -o sleepgo .
go test ./...
go test -race ./...

The test suite covers duration parsing against the forms GNU accepts and rejects, option parsing, bar rendering from 20 to 200 columns, and the waiting itself: that it waits long enough, that ticking for the display does not push it past the deadline, and that it can be interrupted and suspended correctly.

Screenshots in this README are captured from real terminal sessions rather than assembled by hand.

Platform support

Linux, macOS and the BSDs get everything, and Linux is what the project is tested on. Windows compiles and sleeps correctly; terminal width there comes from $COLUMNS, and the Ctrl-Z handling has no Windows equivalent so it is skipped. The Windows display has had no real-world testing, and reports are welcome.

License

MPL-2.0.

About

sleep(1) with a progress bar. A drop-in replacement for GNU sleep — same duration operands, silent by default — that can also show a bar, a countdown, or the wake-up time when you ask for one. Single Go binary, no dependencies outside the standard library.

Topics

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages