3 min read

On [

The other day my friend Will asked me to execute which [ into a shell. Of course, I dropped everything I was doing and ran to a terminal:

~:$ which [
/usr/bin/[

~:$ type -a [
[ is a shell builtin
[ is /usr/bin/[

~:$ type -t [
builtin

Say what?

man [ told me that it was essentially the test command, which I had heard of, though frankly, had never used.

~:$ type -a test
test is a shell builtin
test is /usr/bin/test

Further, neither one is a symlink:

~:$ ll /usr/bin/{[,test}
-rwxr-xr-x 1 root root 51K Feb 22  2017 /usr/bin/[
-rwxr-xr-x 1 root root 47K Feb 22  2017 /usr/bin/test

type above shows that there are two locations for both [ and test. Why?

Explanation

It turns out that the Bourne shell did not have either [ or test as a builtin, so they were included as system binaries. However, the Bourne shell on my Debian system does indeed include both commands as builtins, so I’m not sure why they are still there (although my guess would be in case there is a shell lurking about that doesn’t include them as builtins).

Given a choice, I’ll use the Bash builtin, as it’s faster and there is no context switch between the shell and the binary.

Examples

I use test a lot now instead of the “change, save and execute” development loop when I get any errors when running a Bash script.

Since the test and [ executables will return a value like any other binary, it is necessary to check their return values to see if the tests were successful.

test example:

~:$ test 1 -eq 1
~:$ echo $?
0
~:$ test 1 -eq 2
~:$ echo $?
1

[ example:

~:$ [ 1 -eq 1 ]
~:$ echo $?
0
~:$ [ 1 -eq 2 ]
~:$ echo $?
1

Conditional examples:

~:$ if test 1 -eq 1
> then
> echo foobar
> fi
foobar

~:$ if [ 1 -eq 1 ]
> then
> echo foobar
> fi
foobar

Although these examples were run from the terminal, they obviously work in a Bash script as well.

Differences

The biggest (only?) syntactic difference is that [ needs a closing option ], which most developers would be inclined to add anyways. In fact, prior to this post, I had never thought of Bash’s use of [ as being an actual command! Weeeeeeeeeeeeeeeeeeeeeee

Also, note that test does not recognize any options, because it would not be able to know the difference between them and what it is testing.

For example, you cannot get any help or version information by typing the following:

test --help
test --version

Either bring up its man page (man test), or get the information from [:

~:$ /usr/bin/[ --version
[ (GNU coreutils) 8.26
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Written by Kevin Braunsdorf and Matthew Bradburn.

Conclusion

While I do use test as I’ve mentioned, I was more interested in writing this post because of the oddity of the /usr/bin/[ binary. I do recall seeing it on my system before but never really gave it much thought. There are so many interesting corners of GNU/Linux!

References