Some commands take a while. When I’m running a large test suite, re-seeding a database or installing dependencies, I often switch away from the terminal window to do something else. But I like to know when the task is finished.
The simplest solution is to run two commands: the first to do the job, and the second to notify me. For example:
mix run priv/repo/seeds.exs; say "finished seeding"
(
say
is a MacOS command that reads aloud whatever text it’s given.
On Linux, espeak
works the same way.
)
I also like to be told whether the command succeeded or failed. Assuming it sets the exit code properly, that’s easy to check:
some_command && say "success" || say "failure"
Gold-plating
The solution above is probably all you need. But in my own usage, I’ve made two small improvements.
First, I use this trick often enough that I wanted a shorthand.
judge some_command
is the name I chose.
Second, I wanted to be able to use it as an afterthought, like some_command; judge
.
For example, if I start running some_command
and then realize it’s going to take a while, I can type judge
and press enter, so that when the first command finishes, judge
will tell me whether it succeeded or failed.
So in my shell configuration (.zshrc
, since I use Zsh), I define a function like this:
# This is for bash-like shells; for a fish shell port, see
# https://github.com/iamvery/dotfiles/blob/master/.config/fish/functions/judge.fish
#
# Usage:
# - with args, `judge mix test`; runs `yay` or `boom`
# depending on exit status of given command
# - without args, `mix test; judge`; runs `yay` or `boom`
# depending on exit status of previous command
function judge() {
last_exit_status=$?
number_of_args=$#
if [ $number_of_args -gt 0 ]
then
# - treat the args as a command to run
# - $@ is all the args given
# - `"$@"` makes sure that quoting is preserved;
# eg, if the command was `judge echo one "two three"`,
# `echo` will get two args, not three
# - Once the expansion is done, the shell sees a bare
# command and runs it.
"$@" && yay || boom
else
# No args given means no command to run, so check the exit
# status of the last command and notify accordingly
[ $last_exit_status -eq 0 ] && yay || boom
fi
}
This function runs yay
for successful commands and boom
for failed ones.
I’ve defined yay
and boom
as shell scripts on my path.
Each plays an audio clip, using a backgrounded command (ending with &
) so that it immediately returns control to the shell.
yay
plays applause and boom
plays an explosion sound.
And each sets an appropriate exit code to allow further chaining via &&
or ||
.
Here is yay
:
#!/bin/bash
afplay ~/.dotfiles/other_files/sounds/yay.mp3 -v -0.2 &
exit 0
And here’s boom
:
#!/bin/bash
afplay ~/.dotfiles/other_files/sounds/boom.mp3 -v 0.7 &
exit 1
Of course, these scripts could play a randomly-chosen sound file, display a visual notification, or anything else that would get your attention.
Happy coding!