Bash : and [ are shell commands

 · 2 min · torgeir

The obscure workings of bash never cease to amaze me. The commands : and [ are two bash commands I never knew was runnable on their own.

Terminal Bash

Edits:

  • [2024-01-04 tor]: Its actually even more weird. Modern shells have a built-in implementation of [, but also usually include the program /bin/[ for historical reasons.

But hey, they are!

The colon

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.

Says earl on Stack Overflow.

And what do you know

: && echo ohye || echo nop
ohye

There are lots of creative use cases in the Stack overflow post, that never ocurred to me. I did however stumble across that if statements need an expression inside the truthy branch to work, as I eagerly commented out some unescessary code.

This works.

bash
if [[ 1 == 1 ]]; then
  echo yes
fi
yes

This does not!

bash
if [[ 1 == 1 ]]; then
  # echo no
fi
bash: syntax error near unexpected token `fi'

Should have used the colon :!

Some even sugggest you can use them for disabling variable commands or when you only want parameter expansions just for their side-effects! 😵‍💫

And when you’re like “Wat, that’s weird.” - meet the single square bracket!

The single square bracket

On macos, try this

ls -la /bin/ | head -n 5
total 4788
drwxr-xr-x 39 root wheel    1248 des 15 15:43 .
drwxr-xr-x 20 root wheel     640 des 15 15:43 ..
-rwxr-xr-x  2 root wheel  134224 des 15 15:43 [
-r-xr-xr-x  1 root wheel 1310224 des 15 15:43 bash

Say what?

which [
[: shell built-in command

Try it. (Edit: Note that this is not the built-in, but the equivalent [-program of the shell)

"/bin/["
[: [missing ]

Its also called test. The manual page leads with

TEST(1)                General Commands Manual                TEST(1)

NAME
test, [ — condition evaluation utility

SYNOPSIS
test expression
[ expression ]

DESCRIPTION
The  test  utility evaluates the expression and, if it evaluates to true,
returns a zero (true) exit status; otherwise it returns  1  (false).   If
there is no expression, test also returns 1 (false).

So here’s an expression, let’s try it

"/bin/[" 1 = 1
[: missing ]

But it fails, as the final argument must be].

"/bin/[" 1 = 1 ] && echo works
works

And as you’d expect, the following test fails

"/bin/[" 1 = 2 ] && echo works || echo fails
fails

The gift of bash just keeps on giving! 🎁