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?
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?
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
case a in a ) ;; esac
. Are there some shells which don't accept this?
case ${var} in value);; *) do_something;; esac
is acceptable. The :
command is not needed for empty cases.
May 7, 2012 at 18:47
true
seems like a non-sequitur without reading some of the other answers first.
Apr 9, 2020 at 0:17
:
is handy is when :=
to assign shell variables, like : ${FOO:=bar}
Jun 19, 2020 at 19:14
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.
:
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.
while true; do ...; done
communicates intentions to the reader better than while :; do ...; done
May 7, 2012 at 18:31
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".
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
!
in front of [ some-exotic-condition ]
is goofy, but a superfluous : else
after it is not goofy.
!
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/…
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.
There are two cases where I find :
useful:
#!/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.
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:
Put the condition in a function:
exotic_condition() { [ -f foo ] && bar || baz; }
if ! exotic_condition; then
do_something_here
fi
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:
log() { printf '%s\n' "$*"; }
error() { log "ERROR: $*" >&2; }
fatal() { error "$@"; exit 1; }
[ -f foo ] && bar || baz || fatal "condition not met"
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.
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.
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 :.
:
is required to be a built-in, whiletrue
is not, which affects the scope of variables