Long-Running Command Alerts

If you’re a developer or power-user, then you probably have to run commands that take a reasonably long time to complete fairly frequently. Too long to comfortably stare at your screen waiting for it to complete in any case. I like to be able to be alerted to the fact that one of these long-running commands has finished, so that I can do something else while it’s running, but then get right back to it once it’s done. In this post I outline the two ways I do this: aurally, and visually (via desktop notifications).

Everything in this post runs on Linux - I don’t believe the approaches outlined here would work for macOS, Windows, or most other operating systems. At the time of writing this I’m uses these commands on Linux Mint with Bash as my shell.

Aurally #

I define a shell function tone like so:

tone() {
    timeout -k ${2} ${2} speaker-test --frequency ${1} --test sine;
    return 0
} > /dev/null 2>&1

It takes 2 arguments: frequency, and duration. It uses the command speaker-test provided by ALSA, which plays a sine wave with the specified frequency until the process is killed. To kill the process so that we aren’t subjected to an endless pure tone we use the command timeout provided by the GNU Core Utilities. We use > /dev/null 2>&1 to discard the text output.

I then define an alias named beep:

alias beep='tone 523.25 0.35'

When run, this plays a nice C5 for 0.35 seconds. Feel free to experiment with different frequencies/notes, and durations. Note that speaker-test will only play sine waves with a frequency between 30 Hz and 8000 Hz by default, but you probably want to stay without that range anyway.

NoteFrequency (Hz)
C016.35
C#0/Db017.32
D018.35
D#0/Eb019.45
E020.60
F021.83
F#0/Gb023.12
G024.50
G#0/Ab025.96
A027.50
A#0/Bb029.14
B030.87
C132.70
C#1/Db134.65
D136.71
D#1/Eb138.89
E141.20
F143.65
F#1/Gb146.25
G149.00
G#1/Ab151.91
A155.00
A#1/Bb158.27
B161.74
C265.41
C#2/Db269.30
D273.42
D#2/Eb277.78
E282.41
F287.31
F#2/Gb292.50
G298.00
G#2/Ab2103.83
A2110.00
A#2/Bb2116.54
B2123.47
C3130.81
C#3/Db3138.59
D3146.83
D#3/Eb3155.56
E3164.81
F3174.61
F#3/Gb3185.00
G3196.00
G#3/Ab3207.65
A3220.00
A#3/Bb3233.08
B3246.94
C4261.63
C#4/Db4277.18
D4293.66
D#4/Eb4311.13
E4329.63
F4349.23
F#4/Gb4369.99
G4392.00
G#4/Ab4415.30
A4440.00
A#4/Bb4466.16
B4493.88
C5523.25
C#5/Db5554.37
D5587.33
D#5/Eb5622.25
E5659.25
F5698.46
F#5/Gb5739.99
G5783.99
G#5/Ab5830.61
A5880.00
A#5/Bb5932.33
B5987.77
C61046.50
C#6/Db61108.73
D61174.66
D#6/Eb61244.51
E61318.51
F61396.91
F#6/Gb61479.98
G61567.98
G#6/Ab61661.22
A61760.00
A#6/Bb61864.66
B61975.53
C72093.00
C#7/Db72217.46
D72349.32
D#7/Eb72489.02
E72637.02
F72793.83
F#7/Gb72959.96
G73135.96
G#7/Ab73322.44
A73520.00
A#7/Bb73729.31
B73951.07
C84186.01
C#8/Db84434.92
D84698.63
D#8/Eb84978.03
E85274.04
F85587.65
F#8/Gb85919.91
G86271.93
G#8/Ab86644.88
A87040.00
A#8/Bb87458.62
B87902.13

If you want to have a different tone play for success versus failure, you can do so like so:

alias beep='tone `[ $? = 0 ] && echo 523.25 || echo 261.63` 0.35'

We get the exit code of the last executed command with $?, and check if it was 0 (success) with [ $? = 0 ]. If it was a success, the frequency argument is set to C5 (523.25 Hz). If it was a failure (i.e. a non-zero exit code), the frequency argument is set to C4 (261.63 Hz).

To hear the difference, set the alias, then run the following:

true; beep
false; beep

If you’re unfamiliar with the true and false commands, they’re a fun little part of the GNU Core Utilities. Their docs sum them up nicely:

true - do nothing, successfully

false - do nothing, unsuccessfully

Visually #

Sometimes I’m not wearing my headphones, or otherwise can’t hear audio from my computer. Or sometimes I want to run multiple long-running commands, and have them all alert me when they’re done, but not be subjected to a cacophony of pure tones. In these cases I use desktop notifications. They don’t grab my attention quite as well as audio usually does, but are better than nothing.

For this, Bash comes with the alert alias defined like so:

notify-send --urgency=low -i "$([ $? = 0 ] && echo terminal || echo error)" "$(history|tail -n1|sed -e 's/^\s*[0-9]\+\s*//;s/[;&|]\s*alert$//')"

Let’s unpack this:

With this, we can use it in much the same way as beep:

true; alert
false; alert

We can also include a custom message:

true; alert '`true` exited with exit code 1, unsurprisingly'

Concerns About Long-Running Commands #

It’s all well and good that we can alert ourselves with these methods that a long-running command has terminated, but there’s something to be said for switching tasks while a command runs. It’s well known that we humans are rubbish at switching tasks - especially mentally demanding ones. When a command takes a very long time to run, this isn’t much of a problem, as you can fully switch away from your task involving the command to immerse yourself in something else. It becomes more of an issue when a command takes long enough that just waiting for it to finish while doing nothing else is unpleasant, i.e. long enough to warrant using one of the methods discussed here to alert yourself that it’s done so that you can do something else. You don’t have enough time with these commands to fully switch over to another demanding task, and what’s worse, by switching away from the mental state you were in when you issued the command you’ll have a hard time coming back to it. There have been many times where I’ve had to wait for around 3-10 minutes for some command (.e.g a compilation command) to complete, then switch back to a mental state suitable to work on whatever I was working on before, then run the command again. Repeat all day long. It’s exhausting, and difficult to get much good work done like this.

I find that listening to music can help make sitting through the long-running commands easier. By intentionally not engaging in anything else, and keeping my mind from wandering to other topics, I can re-engage with the task at hand much more quickly after the command terminates. What kind of music you use matters, as music does impose a cognitive load. I find that lyricless music has a lesser cognitive load than music with lyrics in a language that I do not understand, and music with lyrics in a language that I do not understand has a lesser cognitive load than music with lyrics in a language I do understand.

An excellent option for maximizing productivity and mental wellbeing is to meditate while the command runs. I do this sometimes, but have trouble bringing myself to do it regularly, even though I’ve observed it’s great at keeping my mind prepared to get back to the task I was just working on, and results in other benefits.

If you are going to do something else with the time taken by the long-running command, try to do something that isn’t very mentally demanding or stimulating - or at least uses different parts of the brain. Exercise is a good option, as well as certain monotonous chores, such as washing dishes.

Whatever you do, try to avoid sites like Reddit, Hacker News, Twitter, and anything else that can effectively grab your attention. Phones can be pretty bad about this too, as can message boards, chatrooms, and chatting with friends in-person. Reflect on what has a tendency to grab and hold onto your attention, and then try to avoid all of those things if you want to stay on-task through a long-running command.