54

I've hacked on a lot of shell scripts, and sometimes the simplest things baffle me. Today I ran across a script that made extensive use of the : (colon) bash builtin.

The documenation seems simple enough:

: (a colon)  
     : [arguments]  

Do nothing beyond expanding arguments and performing redirections. The return status is zero.

However I have previously only seen this used in demonstrations of shell expansion. The use case in the script I ran across made extensive use of this structure:

if [ -f ${file} ]; then
    grep some_string ${file} >> otherfile || :
    grep other_string ${file} >> otherfile || :
fi

There were actually hundreds of greps, but they are just more of the same. No input/output redirects are present other than the simple structure above. No return values are checked later in the script.

I am reading this as a useless construct that says "or do nothing". What purpose could ending these greps with "or do nothing" serve? In what case would this construct cause a different outcome than simply leaving off the || : from all instances?

ilkkachu
  • 138,973
Caleb
  • 70,105

7 Answers7

54

The : builtin is also useful with the Bash "assign default values" shell expansion, where the expansion is often used solely for the side effect and the value expanded is thrown away:

# assign FOO=bar iff FOO is unset or empty
: "${FOO:=bar}"
ilkkachu
  • 138,973
Joni
  • 663
33

It appears the :s in your script are being used in lieu of true. If grep doesn't find a match in the file, it will return a nonzero exit code; as jw013 mentions in a comment, if errexit is set, probably by -e on the shebang line, the script would exit if any of the greps fail to find a match. Clearly, that's not what the author wanted, so (s)he added || : to make the exit status of that particular compound command always zero, like the more common (in my experience) || true/|| /bin/true.

Kevin
  • 40,767
  • Duh. This was in the concept of an RPM build script, and while I didn't see any exit code checking inside the script, I forgot to consider that the parent process might be watching. – Caleb Feb 14 '12 at 20:55
  • 9
    If that's the case, I'd call it poor scripting practice. It's functionally equivalent to using true instead, but the semantic intent is much clearer with true. : is more suitable when an explicit NOP is desired. – jw013 Apr 27 '12 at 06:12
  • In my experience, this is a common practice in RPM scripts. It probably shouldn't be, but there we are. – mattdm Dec 15 '17 at 18:46
  • some purists prefer : instead of true because : is a bash built-in where as true is usually a compiled binary with more overhead. usually i use true because the code is more readable (likewise I prefer to use source instead of .). – Trevor Boyd Smith Jan 18 '19 at 13:26
  • @jw013 : as true is known because of while :; do... it's extremely common knowledge abong bash users and just as good practice as ternary operators. I'm here because I saw it being used in the way @Joni is using it. – Ray Foss Sep 11 '20 at 01:45
  • @TrevorBoydSmith, Bash 3.2 already has true as a builtin. Busybox does say that : is a "special shell builtin" and true a (regular) "shell builtin", but I'm not sure what the difference would be, at least here. – ilkkachu Mar 08 '21 at 11:37
28

The : built-in was already in the Thompson shell — it's documented for Unix V6 in 1975. In the Thompson shell, : indicated a label for the goto command. If you never attempted to call goto on a line beginning with , that line was effectively a comment.

The Bourne shell, the ancestor of Bourne/POSIX shells as we know them, never had a goto that I know of, but retained : as a no-op command (it was already present in Unix V7), and it was commonly used for comments (an actual comment syntax with # was only added in a later version).

24

I can think of two places I've used : in the past.

while :
do
     shell commands
     some exit condition
done

That is a forever-loop.

function doSomethingStub {
    :
}

Put in a stub function, just to get top level flow of control correct.

One use I've seen back in the Old Days: Instead of a #!/bin/sh (or whatever) line, you'd see a : line. Some of the older Real Unix kernels or Real Unix shells would use that to mean "I'm a shell script, have sh run me". As I recall this was just when csh was making inroads as a common interactive shell. See: http://www.faqs.org/faqs/unix-faq/faq/part3/section-16.html

ilkkachu
  • 138,973
  • @BruceEdiger: Do you have a reference to the "shecolon" line? – l0b0 Mar 22 '12 at 08:20
  • 8
    Finally found a ": at the beginning of a script" reference and explanation: http://www.faqs.org/faqs/unix-faq/faq/part3/section-16.html –  Mar 22 '12 at 20:22
  • 1
    the behavior at the start with : is very strange. i found that it does indeed cause the script to be run with sh. where as when you start the script without a shebang... it tries to run the script with whatever shell is running (so if you are running bash it tries to run it as bash if you have csh then it tries to run it as csh). – Trevor Boyd Smith Jan 18 '19 at 14:04
19

I dug out an old reference: "The UNIX Programming Environment" (c) 1984 by Kernighan and Pike.

Page 147 (Shell Programming) says this:

":" is a shell built-in command that does nothing but evaluate its arguments and return "true". Instead [referring to a script example], we could have used true, which merely returns a true exit status. (There is also a false command.) But ':' is more efficient than true because it does not execute a command from the file system. [Italics/emphasis is mine.]

guntbert
  • 1,637
15

":" is handy for debugging.

DEBUGLOG=": debugfunction"

statement
statement
$DEBUGLOG arg1 arg2 ...
statement
statement

Running normally the debugfunction is never executed so sh just steps over the noop (variables and wildcards are expanded though). If more in depth debugging is required, remove the noop from the variable and debugfunction is called with whatever arguments are required.

Another handy use is as a block comment, which is a missing feature from shell syntax.

: << COMMENT
all --statements --in --here
are now -a here document which are
passed to --the noop
COMMENT
Colin
  • 309
9

I seem to recall that early versions of the shell didn't have a comment syntax. A line starting with : (which probably would have been an actual executable, similar to /bin/true) would have been the best alternative.

Here's a man page for the ancient Thompson shell (no relation); there's no mention of any comment syntax.

  • 6
    The origin of : was in fact a label indicator for the goto command, in some ancient shell (I don't know which). A label : something could indeed be used as a comment, if there was no matching goto. The practice stuck even after goto disappeared. – Gilles 'SO- stop being evil' Feb 15 '12 at 06:29