A shell colon does nothing. Use it anyway
The Power of Nothing: Why the Shell Colon is Actually Useful
I have authored a countless number of shell scripts over the years, yet I am still frequently blindsided by simple tricks that leave me wondering how I didn't know them sooner.
Metadata
| Published | Modified | Author | Tags |
|---|---|---|---|
| 23 July 2026 | 26 July 2026 | Filip Roséen | #shell, #posix, #unix |
☕ A Scene from the Digital Trenches
Imagine this: a desk featuring a cup of coffee that has gone ice-cold sitting right next to a fresh brew that is far too hot to drink. There are four different terminal windows open—three of which should have been killed an hour ago—and a shell script that I was dreading having to write.
In the midst of this chaos, I discovered that a single character—the colon (:)—could save my night.
🛡️ Validating Mandatory Arguments
Most of us have a "muscle memory" approach to checking if a script received the necessary inputs. Usually, it looks like this:
if [ -z $1 ]; then echo "missing argument, aborting"; exit 1; fi
But what if you could condense those four lines into a single, elegant expression?
# Example: example.sh
: ${1:?missing argument, aborting.}
The Result:
$ bash example.sh
example.sh: line 1: 1: missing argument, aborting.
If you use a named variable instead of a positional parameter, the diagnostic becomes even clearer because the shell includes the variable's name:
# greet.sh
: ${GREET_NAME:?missing argument, aborting.}
echo "Hello $GREET_NAME!"
$ bash greet.sh
greet.sh: line 1: GREET_NAME: missing argument, aborting.
How it Works
There are two distinct mechanisms interacting here:
- Parameter Expansion: The syntax
${name:?diagnostic}checks if a variable is either unset or null.- If empty/unset print the diagnostic to
stderrand exit with a status . - If set it simply expands to the value of the variable.
- If empty/unset print the diagnostic to
- The Null Command: The leading colon.
🔍 What exactly is the "Other" Colon?
You might be wondering about the colon sitting at the start of the line.
The : is known as the null-command. It is a shell builtin that evaluates all its arguments and then promptly throws the result away. It is an ancient relic, dating back to the 1971 Thompson shell, where it served a dual purpose as both a label and the original Unix comment marker.
🚀 The Null-Command in the Limelight
To demonstrate that the null-command is more than just a placeholder, consider these mind-blowing applications:
- Setting Defaults:
: ${DATA_DIR:=/var/data}(The colon swallows the result of the assignment). - Default Retries:
: ${RETRIES:=3}. - File Truncation:
: error.log(Truncates the file). - Multi-file Truncation:
: error.log access.log. - Readability Check:
( : dataset.json ) echo YES(Checks if the file is readable). - Interrupt Handling:
trap : INT(Sincetraprequires a command,:provides a no-op). - Unset Variable Validation:
set -u # Treat unset variables as errors : $DEPLOY_ENV $HOST # Validates existence without executing them - Filling Logic Gaps:
if some-command; then : # A command is required here, but we want nothing to happen else echo "command failed" fi
🏁 Con-colon-sion
If you share my desire to minimize keystrokes (because we've got to go fast), the combination of the null-command and parameter expansion is a powerful duo.
Try this safe local experiment:
set : : : : : : : : : : : : : : : : : : : : :
while : colons are more than ${1:?magic} ; do
echo $*
shift
done
❓ Frequently Asked Questions
Why do I need the null-command for parameter expansion?
If you omit the colon, the shell evaluates the expansion and then tries to execute the resulting string as a command.
# Without the colon:
% ${HELLO:=123}
zsh: command not found: 123
# With the colon:
% : ${HELLO:=123}
% echo $HELLO
123
Why use : ${VAR:=val} instead of VAR=${VAR:-val}?
This is largely a matter of preference, but the colon method reduces the chance of typos by mentioning the variable name only once.
| Method | Variable Mentions | Risk |
|---|---|---|
: ${DATA_DIR:=/var/data} | 1 | Low |
DATA_DIR=${DATA_DIR:-/var/data} | 2 | Higher (e.g., DATA_DIR=${DATA_DRI:-...}) |
Doesn't this hurt readability?
Some argue that using : in an if statement is contrived. For example, one could simply use if ! some-command. However, the goal of that example isn't to suggest that you should always use it in if blocks, but to illustrate that whenever a command is syntactically required but no action is desired, the null-command is your tool.
🌍 Real-World Application: The Git Trick
A user named ifphilipe on Hacker News shared a brilliant use case for the null-command:
I use the colon as EDITOR with Git when I want to do an interactive rebase combined with auto squash without having to edit the todo list.
I have an alias for that which I call a quick interactive rebase:
alias riq='git -c sequence.editor=: rebase --interactive'
Reflection: While the if statement example in this article was debated, this Git alias is a perfect, practical example of needing a command that does absolutely nothing.
Final Checklist for Shell Mastery
- Understand
${VAR:?msg} - Recognize the
:null-command - Apply
: ${VAR:=val}for defaults - Use
EDITOR=:to skip Git edits