17

In bash:

$ type :
: is a shell builtin
$ type true
true is a shell builtin

Looks like they are the same, but they don't give the same system trace:

$ strace :
strace: :: command not found
$ strace true
execve("/bin/true", ["true"], [/* 82 vars */]) = 0
[snip]
exit_group(0)                           = ?

I tried diffing strace bash -c : 2>:.txt and strace bash -c true 2>true.txt, but couldn't find any differences between them except for the memory locations.

In dash:

$ type :
: is a special shell builtin
$ type true
true is a shell builtin

OK, so they are not the same. help : and help true aren't very useful, and they return the same in bash and dash. Is there any practical difference at all between them, except that : saves three bytes and makes scripts less readable?

l0b0
  • 50,672
  • 41
  • 197
  • 360
  • 3
    Related [question about the purpose of the colon builtin](http://unix.stackexchange.com/questions/31673/what-purpose-does-the-colon-builtin-serve). – jw013 Mar 22 '12 at 02:35
  • See also [Which is more idiomatic in a bash script: \`|| true\` or \`|| :\`?](https://unix.stackexchange.com/q/78408) – Stéphane Chazelas Sep 25 '22 at 14:00

3 Answers3

28

There's no real difference in behavior. Both commands do nothing and exit with a successful status. : emphasizes doing nothing; true emphasizes the successful status.

strace true works because true is both a shell builtin and an external command (/bin/true); : is only a shell builtin (there's no /bin/: -- though there could be, and probably was on very old Unix systems). In bash, try

type -a :
type -a true

The reasons that both exist are historical. If I recall correctly, some very early shells didn't have a comment syntax, so the do-nothing : command was used instead.

There is some internal difference in dash. Looking through the source, available at git://git.kernel.org/pub/scm/utils/dash/dash.git, shows some different code paths in eval.c, but I haven't been able to produce any visibly different behavior other than the word special in the output of type :.

Keith Thompson
  • 21,782
  • 6
  • 48
  • 55
  • 8
    Additionally, early versions of UNIX did not have `/bin/true` or `/bin/false`. Also the `:` command is sometimes used for the argument processing side effects: `: ${num_times:=10}`. – Arcege Mar 21 '12 at 18:16
  • 7
    `:` was [originally a label indicator](http://unix.stackexchange.com/questions/19124/bash-multi-line-command-with-comments-after-the-continuation-character/19126#19126), back in an ancestor of the Bourne shell that had `goto`. Apparently `:` was abused as a comment indicator and stuck. – Gilles 'SO- stop being evil' Mar 21 '12 at 23:40
  • 1
    The behavior of `:` as a label indicator for goto was preserved in Microsoft's pseudo-Unix command line clone, `command.com`, and remains in its successor `cmd.exe`, as does the practice of abusing `::` for a line comment. – Sorpigal Jun 25 '12 at 12:19
8

They're identical in Bash. Look at builtins/colon.def in the Bash-4.2 source code.

In your command strace true you are actually running the binary /bin/true instead of the bash built-in true.

Jodie C
  • 1,869
  • 12
  • 13
6

The difference between the commands are that by definition : is a special built-in utility whereas true is a regular built-in utility in POSIX compliant shells.

According to POSIX specification, special built-ins are treated slightly differently in a way that:

variable assignments preceding the invocation of a special built-in utility remain in effect after the built-in completes; this shall not be the case with a regular built-in or other utility.

This can be illustrated in POSIX compliant shells as follows:

$ VAR=FOO
$ VAR=BAR :
$ echo "$VAR"
BAR
$ VAR=FOO
$ VAR=BAR true
$ echo "$VAR"
FOO

Another aspect of difference is that:

an error in a special built-in utility may cause a shell executing that utility to abort, while an error in a regular built-in utility shall not cause a shell executing that utility to abort.

Example code in action:

$ ( : > ""; echo "You won't see this!" )
sh: 1: cannot create : Directory nonexistent
$  echo "Exit code: $?"
Exit code: 2
$ ( true > ""; echo "Hello!" )
sh: 1: cannot create : Directory nonexistent
Hello!
$ echo "Exit code: $?"
Exit code: 0
Karolis
  • 161
  • 1
  • 3
  • That's a useful distinction. I've even seen it in action, in a very confusing form, to avoid duplication when setting default values: [`: "${VARIABLE:=DEFAULT_VALUE}"`](https://stackoverflow.com/a/28085062/96588). – l0b0 Sep 25 '22 at 13:47
  • @l0b0, using `${var:=value}` is unrelated, though, it'd work with `true` also. (Try e.g. `unset foo; true "${foo:=value}"; echo $foo` with `true` vs. `:`.) The standard text only mentions variable assignments before the command --those that would end up as env vars for the launched command-- not expansions within the command line. – ilkkachu Sep 25 '22 at 13:55
  • 1
    See also `sh -c ': > /; echo x'` vs `sh -c 'true > /; echo x'` and [Which is more idiomatic in a bash script: \`|| true\` or \`|| :\`?](https://unix.stackexchange.com/a/78411) – Stéphane Chazelas Sep 25 '22 at 14:00
  • @StéphaneChazelas Updated my answer to include this aspect too. Thank you! – Karolis Sep 25 '22 at 17:37