452

What is the purpose of a command that does nothing, being little more than a comment leader, but is actually a shell builtin in and of itself?

It's slower than inserting a comment into your scripts by about 40% per call, which probably varies greatly depending on the size of the comment. The only possible reasons I can see for it are these:

# poor man's delay function
for ((x=0;x<100000;++x)) ; do : ; done

# inserting comments into string of commands
command ; command ; : we need a comment in here for some reason ; command

# an alias for `true'
while : ; do command ; done

I guess what I'm really looking for is what historical application it might have had.

amphetamachine
  • 27,620
  • 12
  • 60
  • 72
  • I wouldn't say a command that returns a specific value "does nothing." Unless functional programming consists of "doing nothing." :-) – LarsH Sep 01 '15 at 20:53
  • 1
    One of the uses of the : 'null' program is to annotate Shell sequences. That's according to Ken Thompson in his 1976 paper, The Unix Command Language. https://github.com/susam/tucl/blame/master/the-unix-command-language.md#L218 – amdn Nov 11 '21 at 17:15

12 Answers12

529

Historically, Bourne shells didn't have true and false as built-in commands. true was instead simply aliased to :, and false to something like let 0.

: is slightly better than true for portability to ancient Bourne-derived shells. As a simple example, consider having neither the ! pipeline operator nor the || list operator (as was the case for some ancient Bourne shells). This leaves the else clause of the if statement as the only means for branching based on exit status:

if command; then :; else ...; fi

Since if requires a non-empty then clause and comments don't count as non-empty, : serves as a no-op.

Nowadays (that is: in a modern context) you can usually use either : or true. Both are specified by POSIX, and some find true easier to read. However there is one interesting difference: : is a so-called POSIX special built-in, whereas true is a regular built-in.

  • Special built-ins are required to be built into the shell; Regular built-ins are only "typically" built in, but it isn't strictly guaranteed. There usually shouldn't be a regular program named : with the function of true in PATH of most systems.

  • Probably the most crucial difference is that with special built-ins, any variable set by the built-in - even in the environment during simple command evaluation - persists after the command completes, as demonstrated here using ksh93:

    $ unset x; ( x=hi :; echo "$x" )
    hi
    $ ( x=hi true; echo "$x" )
    
    $
    

    Note that Zsh ignores this requirement, as does GNU Bash except when operating in POSIX compatibility mode, but all other major "POSIX sh derived" shells observe this including dash, ksh93, and mksh.

  • Another difference is that regular built-ins must be compatible with exec - demonstrated here using Bash:

    $ ( exec : )
    -bash: exec: :: not found
    $ ( exec true )
    $
    
  • POSIX also explicitly notes that : may be faster than true, though this is of course an implementation-specific detail.

earl
  • 40,327
  • 6
  • 58
  • 59
  • Did you mean regular built-ins must ***not*** be compatible with `exec`? – Old Pro Apr 28 '12 at 00:27
  • 1
    @OldPro: No, he's correct in that `true` is a regular builtin, but he's incorrect in that `exec` is using `/bin/true` instead of the builtin. – Dennis Williamson May 29 '12 at 13:02
  • 1
    @DennisWilliamson I was just going by the way the spec is worded. The implication is of course that regular builtins should also have a standalone version present. – ormaaj May 31 '12 at 04:47
  • 32
    +1 Excellent answer. I would still like to note the usage for initializing variables, like `: ${var?not initialized}` et al. – tripleee Jan 20 '14 at 07:28
  • 1
    A more or less unrelated follow-up. You said `:` is a special built in and shouldn't have a function named by it. But isn't the most commonly seen example of fork bomb `:(){ :|: & };:` naming a function with name `:`? – Chong Jun 30 '17 at 00:58
  • cross-linking this example: https://unix.stackexchange.com/questions/25945/how-to-check-if-there-are-no-parameters-provided-to-a-command/162421#162421 – Nate Anderson Jan 12 '19 at 19:34
  • @Chong `There usually shouldn't be a regular program named : ` means there will not be an *executable file* like `/bin/:` or `/usr/bin/:` Whereas other non-special builtins *can* have executables (even some surprising ones, f/ex: `/usr/bin/cd`). – DouglasDD Jul 31 '19 at 09:58
  • Another important difference relates to error handling: *"An error in a special built-in utility (e.g. `:`) may cause a shell executing that utility to abort, while an error in a regular built-in utility (e.g. true) shall not cause a shell executing that utility to abort."* ([Source](https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_14), examples added by me). So `: > /no/such/file; echo got here` might not print "got here", while `true > /no/such/file; echo got here` must print "got here". (Another way to create empty files is `> /no/such/file`.) – tom Oct 16 '21 at 10:16
  • @tripleee, I know `${var?not initialized}` is used to check if a variable is unset. May I ask what does it mean adding `:` in front of it? – midnite May 10 '22 at 06:00
  • @midnite The answers here already cover this massively IMHO. If the variable is set, `${var?fnord}` without `:` in front will attempt to run the variable's value as a command, and run `fnord` as a command if not. In particular, [Ormaaj's answer from 2011](https://stackoverflow.com/a/4892546) explains this (though with `=` instead of `?`; but isn't it obvious that it extends to all the cases where you manipulate a variable?) – tripleee May 10 '22 at 06:03
  • Thanks @tripleee. I see it now. The simple explanation is "if we are only interested in the side-effects rather than actually passing their results to a command". But as [spotted by Juliano](https://stackoverflow.com/questions/3224878/what-is-the-purpose-of-the-colon-gnu-bash-builtin/4892546#comment3328015_3224914), will there be any problems if we use it as an argument of `:`? – midnite May 10 '22 at 07:42
  • The remark you link to explains when it could be problematic. If the variable's current value (or your default value) is humongous, you could end up in trouble (and waste some cycles) but this is quite unusual in practice. If the value was already assigned to a variable, I don't think you can run into a new problem you didn't already have. The comment specifically discusses the perils of running a (possibly untrusted) command in backticks. – tripleee May 10 '22 at 07:46
  • empty a file, : > t.txt – Youjun Hu Oct 20 '22 at 01:24
93

I use it to easily enable/disable variable commands:

#!/bin/bash
if [[ "$VERBOSE" == "" || "$VERBOSE" == "0" ]]; then
    vecho=":"     # no "verbose echo"
else
    vecho=echo    # enable "verbose echo"
fi

$vecho "Verbose echo is ON"

Thus

$ ./vecho
$ VERBOSE=1 ./vecho
Verbose echo is ON

This makes for a clean script. This cannot be done with '#'.

Also,

: >afile

is one of the simplest ways to guarantee that 'afile' exists but is 0 length.

Kevin Little
  • 12,436
  • 5
  • 39
  • 47
89

A useful application for : is if you're only interested in using parameter expansions for their side-effects rather than actually passing their result to a command.

In that case, you use the parameter expansion as an argument to either : or false depending upon whether you want an exit status of 0 or 1. An example might be

: "${var:=$1}"

Since : is a builtin, it should be pretty fast.

Benjamin W.
  • 46,058
  • 19
  • 106
  • 116
ormaaj
  • 6,201
  • 29
  • 34
  • 4
    You can also use it for side-effects of arithmetic expansion: `: $((a += 1))` (`++` and `--` operators do not need to be implemented according to POSIX.). In bash, ksh and possible other shells you can do also: `((a += 1))` or `((a++))` but it is not specified by POSIX. – pabouk - Ukraine stay strong Jun 29 '15 at 23:06
  • @pabouk Yep that's all true, though `(())` is specified as an optional feature. "If a character sequence beginning with "((" would be parsed by the shell as an arithmetic expansion if preceded by a '$', shells which implement an extension whereby "((expression))" is evaluated as an arithmetic expression may treat the "((" as introducing as an arithmetic evaluation instead of a grouping command." – ormaaj Jun 30 '15 at 23:35
  • Note that any expansion can be used for its side-effects (even if it doesn't set a variable explicitly) since it can be accessed by `$_` in the next line. So a sequence of `:` commands can be used to successively transform a value. – pyrocrasty Oct 09 '21 at 23:03
  • I only learned about `:` when I saw it used for this in somebody else's script. It's one of those techniques that should be banned because it's just too clever. – Mark Ransom Sep 01 '22 at 20:39
64

: can also be for block comment (similar to /* */ in C language). For example, if you want to skip a block of code in your script, you can do this:

: << 'SKIP'

your code block here

SKIP
Rob Bednark
  • 25,981
  • 23
  • 80
  • 125
zagpoint
  • 649
  • 5
  • 2
42

Two more uses not mentioned in other answers:

Logging

Take this example script:

set -x
: Logging message here
example_command

The first line, set -x, makes the shell print out the command before running it. It's quite a useful construct. The downside is that the usual echo Log message type of statement now prints the message twice. The colon method gets round that. Note that you'll still have to escape special characters just like you would for echo.

Cron job titles

I've seen it being used in cron jobs, like this:

45 10 * * * : Backup for database ; /opt/backup.sh

This is a cron job that runs the script /opt/backup.sh every day at 10:45. The advantage of this technique is that it makes for better looking email subjects when the /opt/backup.sh prints some output.

Flimm
  • 136,138
  • 45
  • 251
  • 267
  • Where is the default log location? Can I set the log location? Is the purpose more for creating output in the stdout during scripts / background processes? – domdambrogia Mar 13 '19 at 16:50
  • 2
    @domdambrogia When using `set -x`, the printed out commands (including something like `: foobar`) go to stderr. – Flimm Mar 13 '19 at 18:31
40

It's similar to pass in Python.

One use would be to stub out a function until it gets written:

future_function () { :; }
Dennis Williamson
  • 346,391
  • 90
  • 374
  • 439
37

If you'd like to truncate a file to zero bytes, useful for clearing logs, try this:

:> file.log
Ahi Tuna
  • 1,253
  • 2
  • 14
  • 26
  • 23
    `> file.log` is simpler and achieves the same effect. – amphetamachine Jun 23 '11 at 17:44
  • 71
    Yah, but the happy face is what does it for me :> – Ahi Tuna Jul 26 '11 at 22:24
  • 28
    @amphetamachine: `:>` is more portable. Some shells (such as my `zsh`) auto-instantiate a cat in the current shell and listen for stdin when given a redirect with no command. Rather than `cat /dev/null`, `:` is much simpler. Often this behavior is different in interactive shells rather than scripts, but if you write the script in a way that also works interactive, debuging by copy-paste is much easier. – Caleb Feb 14 '12 at 22:42
  • 3
    How does `: > file` differ from `true > file` (aside from the character count and the happy face) in a modern shell (assuming `:` and `true` are equally fast)? – Adam Katz Nov 20 '15 at 23:47
  • @AdamKatz Assuming `:` and `true` are equally fast, they are the same. But that isn't a valid assumption. – Tripp Kinetics May 14 '21 at 18:25
  • @TrippKinetics: There *is* a difference between `: > file` and `true > file`. If there is an error, `: > file` can cause the shell to abort. See [my comment](https://stackoverflow.com/questions/3224878/what-is-the-purpose-of-the-colon-gnu-bash-builtin#comment123011791_3224910) for details. – tom Oct 16 '21 at 10:26
29

You could use it in conjunction with backticks (``) to execute a command without displaying its output, like this:

: `some_command`

Of course you could just do some_command > /dev/null, but the :-version is somewhat shorter.

That being said I wouldn't recommend actually doing that as it would just confuse people. It just came to mind as a possible use-case.

LarsH
  • 27,481
  • 8
  • 94
  • 152
sepp2k
  • 363,768
  • 54
  • 674
  • 675
  • 30
    This is not safe if the command is going to dump a few megabytes of output, since the shell buffers the output and then passes it as command-line arguments (stack space) to ':'. – Juliano Jul 11 '10 at 23:39
  • 1
    As an aside, this leads to the question, is there a way to throw away the output of a pipe *without* using `/dev/null`? Suppose `/dev/null` doesn't exist. It can be removed from a system, after all... – Tripp Kinetics May 14 '21 at 18:27
  • 1
    @TrippKinetics it's hard to imagine a system that would run without `/dev/null` at all. It's not worth wasting time worrying about. – Mark Ransom Sep 01 '22 at 20:36
  • @MarkRansom Actually, now that I look at it, there's nothing stopping you from ``:`some_command | some_other_command` ``. – Tripp Kinetics Sep 02 '22 at 19:11
  • @MarkRansom Also, a lot of stuff wouldn't necessarily fail without `/dev/null`. Anything that writes to it would just write to a file called `/dev/null` in there. – Tripp Kinetics Sep 02 '22 at 19:14
  • @TrippKinetics you're assuming that the user has permission to create files in /dev. – Mark Ransom Sep 02 '22 at 19:15
  • wouldn't be the same writing `some_command | :` ? besides, note that the stderr would still be visible – cipper Feb 08 '23 at 14:55
21

It's also useful for polyglot programs:

#!/usr/bin/env sh
':' //; exec "$(command -v node)" "$0" "$@"
~function(){ ... }

This is now both an executable shell-script and a JavaScript program: meaning ./filename.js, sh filename.js, and node filename.js all work.

(Definitely a little bit of a strange usage, but effective nonetheless.)


Some explication, as requested:

  • Shell-scripts are evaluated line-by-line; and the exec command, when run, terminates the shell and replaces it's process with the resultant command. This means that to the shell, the program looks like this:

    #!/usr/bin/env sh
    ':' //; exec "$(command -v node)" "$0" "$@"
    
  • As long as no parameter expansion or aliasing is occurring in the word, any word in a shell-script can be wrapped in quotes without changing its' meaning; this means that ':' is equivalent to : (we've only wrapped it in quotes here to achieve the JavaScript semantics described below)

  • ... and as described above, the first command on the first line is a no-op (it translates to : //, or if you prefer to quote the words, ':' '//'. Notice that the // carries no special meaning here, as it does in JavaScript; it's just a meaningless word that's being thrown away.)

  • Finally, the second command on the first line (after the semicolon), is the real meat of the program: it's the exec call which replaces the shell-script being invoked, with a Node.js process invoked to evaluate the rest of the script.

  • Meanwhile, the first line, in JavaScript, parses as a string-literal (':'), and then a comment, which is deleted; thus, to JavaScript, the program looks like this:

    ':'
    ~function(){ ... }
    

    Since the string-literal is on a line by itself, it is a no-op statement, and is thus stripped from the program; that means that the entire line is removed, leaving only your program-code (in this example, the function(){ ... } body.)

ELLIOTTCABLE
  • 17,185
  • 12
  • 62
  • 78
  • Hello, can you explain what `: //;` and `~function(){}`do ? Thank you `:)` – Stphane Feb 20 '16 at 09:52
  • 1
    @Stphane Added a break-down! As for the `~function(){}`, that's a little more complicated. There's [a](http://stackoverflow.com/questions/8305915/function-vs-function) [couple](http://stackoverflow.com/questions/26078006/iife-with-unary-operator-real-world-use-case) other answers on here that touch on it, although none of them really explain it to my satisfaction … if neither of those questions explains it well-enough for you, feel free to post it as a question here, I'll be happy to answer in-depth on a new question. – ELLIOTTCABLE Feb 21 '16 at 03:38
  • 1
    I did not pay attention to `node`. So the function part is all about javascript ! I'm okey with unary operator in front of IIFE. I thought this was bash too in the first place and actually did not really get the meaning of your post. I'm okey now, thank you for your time spent adding «break-down» ! – Stphane Feb 21 '16 at 18:07
  • `~{ No problem. (= }` – ELLIOTTCABLE Feb 21 '16 at 18:07
19

You can also use : to embed documentation in a function.

Assume you have a library script mylib.sh, providing a variety of functions. You could either source the library (. mylib.sh) and call the functions directly after that (lib_function1 arg1 arg2), or avoid cluttering your namespace and invoke the library with a function argument (mylib.sh lib_function1 arg1 arg2).

Wouldn't it be nice if you could also type mylib.sh --help and get a list of available functions and their usage, without having to manually maintain the function list in the help text?

#!/bin/bash

# all "public" functions must start with this prefix
LIB_PREFIX='lib_'

# "public" library functions
lib_function1() {
    : This function does something complicated with two arguments.
    :
    : Parameters:
    : '   arg1 - first argument ($1)'
    : '   arg2 - second argument'
    :
    : Result:
    : "   it's complicated"

    # actual function code starts here
}

lib_function2() {
    : Function documentation

    # function code here
}

# help function
--help() {
    echo MyLib v0.0.1
    echo
    echo Usage: mylib.sh [function_name [args]]
    echo
    echo Available functions:
    declare -f | sed -n -e '/^'$LIB_PREFIX'/,/^}$/{/\(^'$LIB_PREFIX'\)\|\(^[ \t]*:\)/{
        s/^\('$LIB_PREFIX'.*\) ()/\n=== \1 ===/;s/^[ \t]*: \?['\''"]\?/    /;s/['\''"]\?;\?$//;p}}'
}

# main code
if [ "${BASH_SOURCE[0]}" = "${0}" ]; then
    # the script was executed instead of sourced
    # invoke requested function or display help
    if [ "$(type -t - "$1" 2>/dev/null)" = function ]; then
        "$@"
    else
        --help
    fi
fi

A few comments about the code:

  1. All "public" functions have the same prefix. Only these are meant to be invoked by the user, and to be listed in the help text.
  2. The self-documenting feature relies on the previous point, and uses declare -f to enumerate all available functions, then filters them through sed to only display functions with the appropriate prefix.
  3. It is a good idea to enclose the documentation in single quotes, to prevent undesired expansion and whitespace removal. You'll also need to be careful when using apostrophes/quotes in the text.
  4. You could write code to internalize the library prefix, i.e. the user only has to type mylib.sh function1 and it gets translated internally to lib_function1. This is an exercise left to the reader.
  5. The help function is named "--help". This is a convenient (i.e. lazy) approach that uses the library invoke mechanism to display the help itself, without having to code an extra check for $1. At the same time, it will clutter your namespace if you source the library. If you don't like that, you can either change the name to something like lib_help or actually check the args for --help in the main code and invoke the help function manually.
bfontaine
  • 18,169
  • 13
  • 73
  • 107
Sir Athos
  • 9,403
  • 2
  • 22
  • 23
4

I saw this usage in a script and thought it was a good substitute for invoking basename within a script.

oldIFS=$IFS  
IFS=/  
for basetool in $0 ; do : ; done  
IFS=$oldIFS  

... this is a replacement for the code: basetool=$(basename $0)

amphetamachine
  • 27,620
  • 12
  • 60
  • 72
3

Another way, not yet mentioned here is the initialisation of parameters in infinite while-loops. Below is not the cleanest example, but it serves it's purpose.

#!/usr/bin/env bash
[ "$1" ] && foo=0 && bar="baz"
while : "${foo=2}" "${bar:=qux}"; do
    echo "$foo"
    (( foo == 3 )) && echo "$bar" && break
    (( foo=foo+1 ))
done
kvantour
  • 25,269
  • 4
  • 47
  • 72