Originally, it was used to determine that it was a Bourne shell program, as opposed to C compiled program. This was before shebang and multiple scripting languages (csh, perl). You can still run a script starting with just ::

When you want an "unless" statement in shell scripting, you either use a "not" condition, which can look goofy for some of the tests, or you use ':' in the true-clause, with real code in the false-clause.

if [ some-exotic-condition ]
then
:
else
# Real code here
fi

The "exotic condition" could be something you don't want to negate, or that's just a lot clearer if you don't use "negative logic".

It also shows up in scripts generated by autoconf because it's much easier to add a default : for empty branches than it is to figure out how to reverse the condition.
–
Dietrich EppApr 27 '12 at 22:11

1

I can't see how sticking in a ! in front of [ some-exotic-condition ] is goofy, but a superfluous : else after it is not goofy.
–
KazApr 28 '12 at 0:02

@Kaz has a good point. Let's keep in mind that coming up with exotic conditions is difficult at best. If you have to negate all of it, that's one thing, but it might just make the condition less clear. Does a '!' negate the whole condition, or just the first term? Best to have a ':' true clause sometimes.
–
Bruce EdigerMay 3 '12 at 15:18

I've only ever used this in addition to the # character for temporarily commenting out a line, in a situation in which commenting out the line produces a syntax error, due to a defect in the shell grammar of not allowing an empty sequence of commands:

if condition ; then
:# temporarily commented out command
fi

Without the : we have a missing command sequence, which is a syntax error.

Default variable assignments

#!/bin/sh
# set VAR to "default value" if not already set in the environment
: "${VAR=default value}"
# print the value of the VAR variable. Note that POSIX says the behavior
# of echo is implementation defined if the first argument is '-n' or if any
# argument contains a '\', so use printf instead of echo.
printf '%s\n' "VAR=${VAR}"

This is a convenient way to allow users of your shell script to override a setting without editing the script. (However, command-line arguments are better because you don't run the risk of unexpected behavior if the user coincidentally has the variable you use in their exported environment.) Here's how the user would override the setting:

VAR="other value" ./script

The ${VAR=value} syntax says to set VAR to value if VAR isn't already set, then expand to the value of the variable. Since we don't care about the value of the variable just yet, it is passed as an argument to the no-op command : to throw it away.

Even though : is a no-op command, expansion is performed by the shell (not the : command!) before running the : command so the variable assignment still occurs (if applicable).

It would also be acceptable to use true or some other command instead of :, but the code becomes harder to read because the intention is less clear.

The following script would also work:

#!/bin/sh
# print the value of the VAR variable. Note that POSIX says the behavior
# of echo is implementation defined if the first argument is '-n' or if any
# argument contains a '\', so use printf instead of echo.
printf '%s\n' "VAR=${VAR=default value}"

But the above is much harder to maintain. If a line using ${VAR} is added above that printf line, the default assignment expansion has to be moved. If the developer forgets to move that assignment, a bug is introduced.

Put the condition inside curly braces (or parentheses, but parentheses spawn a subshell process and any changes made to the environment inside the subshell won't be visible outside the subshell) before negating:

if ! { [ -f foo ] && bar || baz; } then
do_something_here
fi

Use || instead of if:

[ -f foo ] && bar || baz || {
do_something_here
}

I prefer this approach when the reaction is a simple one-liner, such as asserting conditions:

In the old pre-bourne shell in ancient versions of UNIX, the : command was originally intended for specifying labels for goto (it was a separate command which winds the input to where the label is found, so labels couldn't be a separate syntax that the shell knows about. if was also a separate command.) It soon became used for comments, before there was a comment syntax (# was used for backspace) and these days is around for compatibility as much as anything.