27

In the answer to this question about comments in shell scripting, it is indicated that the : is a null command that explicitly does nothing (but is not to be used for comments).

What would be the utility of a command that does absolutely nothing?

3

9 Answers 9

20

I typically use true in loops; I think it's more clear:

while true; do
    ...
done

The one place I've found that : is really handy is in case statements, if you need to match something but don't want to actually do anything. For example:

case $answer in
    ([Yy]*) : ok ;;
    (*)     echo "stop."; exit 1 ;;
esac
5
  • 4
    I agree. true for a condition, : for a NOP.
    – jw013
    Apr 27, 2012 at 20:34
  • 1
    bash accepts case a in a ) ;; esac. Are there some shells which don't accept this?
    – Kaz
    Apr 28, 2012 at 0:00
  • 1
    @Kaz: According to the POSIX shell grammar, case ${var} in value);; *) do_something;; esac is acceptable. The : command is not needed for empty cases. May 7, 2012 at 18:47
  • The part about using true seems like a non-sequitur without reading some of the other answers first.
    – jamesdlin
    Apr 9, 2020 at 0:17
  • Another place : is handy is when := to assign shell variables, like : ${FOO:=bar} Jun 19, 2020 at 19:14
13

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 ::

$ echo : > /tmp/xyzzy
$ chmod +x /tmp/xyzzy
$ ./xyzzy

It will generally run the script against $SHELL (or /bin/sh).

Since then, the main use is to evaluate the arguments. I still use:

: ${EDITOR:=vim}

to set a default value in a script.

0
10

: is useful for writing loops that must be terminated from within.

while :
do
    ...stuff...
done

This will run forever unless break or exit is called, or the shell receives a terminating signal.

1
  • 5
    I feel that while true; do ...; done communicates intentions to the reader better than while :; do ...; done May 7, 2012 at 18:31
10

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".

4
  • 1
    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. Apr 27, 2012 at 22:11
  • 3
    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.
    – Kaz
    Apr 28, 2012 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.
    – user732
    May 3, 2012 at 15:18
  • The ! token negates an entire command pipe element. while ! grep ... ; do ... done or if ! [ ... ] ; then ... fi. It's basically external to the test/[] syntax. See: pubs.opengroup.org/onlinepubs/9699919799/utilities/…
    – Kaz
    May 3, 2012 at 17:49
8

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.

5

There are two cases where I find : useful:

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.

Something to put in an empty conditional block

Empty conditional blocks should generally be avoided, but they're sometimes useful:

if some_condition; then
    # todo:  implement this block of code; for now do nothing.
    # the colon below is a no-op to prevent syntax errors
    :
fi

Some people argue that having an empty true if block can make code easier to read than negating the test. For example:

if [ -f foo ] && bar || baz; then
    :
else
    do_something_here
fi

is arguably easier to read than:

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

However I believe there are a few alternative approaches that are better than an empty true block:

  1. Put the condition in a function:

    exotic_condition() { [ -f foo ] && bar || baz; }
    
    if ! exotic_condition; then
        do_something_here
    fi
    
  2. 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
    
  3. 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:

    log() { printf '%s\n' "$*"; }
    error() { log "ERROR: $*" >&2; }
    fatal() { error "$@"; exit 1; }
    
    [ -f foo ] && bar || baz || fatal "condition not met"
    
0
2

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.

1

The ":" shell command exists because in the original v1 shell, the Thompson shell, ":" introduced a label that was potentially the target of a goto command.

The Wikipedia Thompson shell article notes that:

The shell's design was intentionally minimalistic; even the if and goto statements, essential for control of program flow, were implemented as separate commands.

V1 Unix source contains that of a standalone goto command. It's got a main() function making it a standalone program. It appears to read through stdin until it finds a ":" character, then it reads zero or more ASCII space characters, then the label. If that's the label it currently looks for, it does an lseek() on stdin.

My other answer to this question is incorrect. I'm not deleting it because it contains a common use of ":" in the shell.

0

In addition to using it as a statement that does nothing, you can use it to comment out single statements by turning them into arguments for :.

2
  • It won't be exactly a comment since : echo write this line > myfile will still create an empty file.
    – Arcege
    Apr 27, 2012 at 18:46
  • 5
    As explained in the link in the question, : is NOT an adequate commenting mechanism.
    – jw013
    Apr 27, 2012 at 18:55

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .