In a land far-far away..

... there was once a far too cold cup of coffee next to a freshly brewed far-too-hot one. Four different terminals where three could have been closed an hour ago, and a shell script which I really (really) did not want to write.

Who would have thought a single colon would be the one to save the day

Note: Want your mind blown straight away? See Other colons worth remembering.

Checking for required arguments

This is a familiar dance, it's pretty much muscle memory by this point. You have a script, it takes a few arguments, and some of them are mandatory; alright, an if-statement like so many times before:

if [ -z "$1" ]; then echo "missing argument, aborting!" 1>&2 exit 1 fi echo "Hello $1!"
Though.. what if I told you the above four lines could be replaced by just... one?

: "${1:?missing argument, aborting!}" echo "Hello $1!"
$ bash example.sh example.sh: line 3: 1: missing argument, aborting! $ bash example.sh refp Hello refp!

        Parameter expansion and the story of `:?`

There are two things going on in the previous snippet, and you are correct in identifying that one part is using parameter expansion:

  • The syntax - ${name:?diagnostic}checks whether- $nameis unset or empty — if it is, the diagnostic is printed to- stderrand the shell exits with a non-zero status, otherwise;
  • if the variable is set, it is equivalent to - $name.

That.. other colon

So that's one colon, but what about that other one, the one who sits alone at the beginning of the line?

  • :is the null command — a builtin that does nothing but evaluate its arguments and discard the result.
  • :is old — it goes all the way back to the 1971 Thompson shell where it doubled as a label and Unix's very first comment marker.
  • :two eyes staring at you in the dark, with love.

Other colons worth remembering

Perhaps we have already established that there is more to : than meets the
eye, but to prove the real magic of the null-command —
here are a few usages that blew my mind.

: "${DATA_DIR:=/var/data}" # set defaults, : swallows the result : "${RETRIES:=3}" # instead of running it as a command
: > error.log # truncate error.log : > error.log > access.log # truncate both error.log and access.log
( : < dataset.json ) && echo YES # is dataset.json readable? ( : >> result.json ) && echo YES # is result.json writable?
trap : INT # trap requires a command sleep 60 # sleep is interruptible
set -u # error on unset variables : "$DEPLOY_ENV" "$HOST" # check DEPLOY_ENV and HOST

Con-colon-sion

So, if you are like me and prefer less typing (gotta go fast) — the null command and parameter expansion are a good pair to remember.

while : colons are more than punctuation; do echo "I love colons" sleep 1 done