How exactly does "/bin/[" work?
Solution 1
The [
command's job is to evaluate test expressions. It returns with a 0 exit status (that means true) when the expression resolves to true and something else (which means false) otherwise.
It's not that it does nothing, it's just that its outcome is to be found in its exit status. In a shell, you can find out about the exit status of the last command in $?
for Bourne-like shells or $status
in most other shells (fish/rc/es/csh/tcsh...).
$ [ a = a ]
$ echo "$?"
0
$ [ a = b ]
$ echo "$?"
1
In other languages like perl
, the exit status is returned for instance in the return value of system()
:
$ perl -le 'print system("[", "a", "=", "a", "]")'
0
Note that all modern Bourne-like shells (and fish
) have a built-in [
command. The one in /bin
would typically only be executed when you use another shell or when you do things like env [ foo = bar ]
or find . -exec [ -f {} ] \; -print
or that perl
command above...
The [
command is also known by the test
name. When called as test
, it doesn't require a closing ]
argument.
While your system may not have a man page for [
, it probably has one for test
. But again, note that it would document the /bin/[
or /bin/test
implementation. To know about the [
builtin in your shell, you should read the documentation for your shell instead.
For more information about the history of that utility and the difference with the [[...]]
ksh test expression, you may want to have a look at this other Q&A here.
Solution 2
I am always surprised that in the folder
/bin
there is a[
program.
You are right to be surprised. That's one of the rare POSIX commands, with the null utility (:
), that doesn't respect the commands file allowed characters convention (portable filename character set).
Is is what is called when we are doing something like:
if [ something ]
?
Precisely but it can be used without the if
too.
By calling the
[
program explicitly in a shell it asks for a corresponding]
, and when I provide the closing bracket it seems to do nothing no matter what I insert between the brackets.
It does nothing visible but it actually does the very same thing than when used with if
, i.e. it sets the exit status to 0 (true) or anything else (false) depending on what you put inside the brackets. It is (for a reason) the same behavior as the test
command; the only difference is that it looks for the ending ]
. See man test
for details.
Needless to say, the usual way about getting help about a program does not work, i.e. neither
man [
nor[ --help
works.
That depends on your Operating System. man [
definitely works for me on a couple of mainstream Gnu/Linux distributions but it doesn't on Solaris.
[ --help
might work or not depending on the implementation as it is breaking the syntax anyway, missing the ending ]
. Moreover, the POSIX standard for the test
/ [
command explicitly rules out all options, including the --
option termination so both [ --help ]
and test --help
need to return true
and be silent by design. Note that what you put inside the brackets or after [
and that look like options (e.g. -f file
, -n string
, and the likes) are not options but operands.
All modern Bourne style shell interpreters (like bash
, ksh
, dash
and zsh
to name a few) implement the test
/[
utility internally as a builtin so when you use them, the right manual page to refer to might be the one of the shell, not the test
one.
Before Unix System III (1981), the Bourne shell didn't implemented the test
utility as a builtin so only the external binary command implementation was available. There wasn't either a [
command (internal or builtin) until Unix System III so for example under Unix Version 7 you had to type:
if test something ; then
…
instead of:
if [ something ] ; then
…
Solution 3
[
is actually more commonly known as test
command. Typical use of this command is simply to evaluate expressions and return their condition - true or false. It is often used in if-then-else-fi
statements, although it can be used outside of if
statements to conditionally run other commands via shell's &&
or ||
operators, like so.
$ [ -e /etc/passwd ] && echo "File exists"
File exists
$ test -e /etc/passwd && echo "File exists"
File exists
More specifically, evaluation is communicated to other commands via exit status. Some programs may choose to output exit status to signify different types of events - program completing successfully, an error of particular type occurring during execution, or syntax errors. In the case of the test
command, there are 0
signifies true, and 1
signifies false. As Stephan pointed out, syntax errors produce exit status of 2
.
Its location depends on your system, and it also explains why you didn't see man page when you did man [
. For instance, on FreeBSD it is under /bin
. On Linux (or in my particular case, Ubuntu 16.04) it is in /usr/bin/
. If you do man [
or man test
on a Linux system, you will see same documentation open. It's also important to note that your shell may have its own implementation of test
.
It should also be noted that this command has issues, which Korn shell's implementation (commonly known as "conditional expression" reference with double square brackets, [[ "$USER" = "root" ]]
) seeks to resolve. This feature also used by other shells such as bash
and zsh
.
Solution 4
Neither
man [
nor[ --help
works
On some distros (e.g. Ubuntu), man [
follows a symlink to test(1)
. On others (e.g. Arch), this is not the case. (But the test
man page does document usage of [
as well)
type -a [
shows you that there's both a shell builtin and an executable.
$ type -a [
[ is a shell builtin
[ is /usr/bin/[
bash-builtin [ --help
just prints an error message. (But since it's a builtin, you can use help [
, or look at the bash man page / docs).
/usr/bin/[ --help
prints full help output (for the GNU Coreutils version), which starts out with:
$ /usr/bin/[ --help
Usage: test EXPRESSION
or: test
or: [ EXPRESSION ]
or: [ ]
or: [ OPTION
Exit with the status determined by EXPRESSION.
--help display this help and exit
--version output version information and exit
and then describes the allowed syntax for EXPRESSION.
This is another way you could have found out that [
and test
are equivalent.
BTW, if you are programming for bash (or writing one-liners interactively), I'd recommend [[
instead of [
. It's better in several ways, see links in Serg's answer.
Solution 5
[
command returns exit-status zero if expression, contained in its arguments, is considered true and non-zero exit-status if expression, contained in its arguments, is considered false. It also fails with error message if its last argument isn't ]
(this is done purely for aesthetic reasons).
E.g.:
[ hello ]
echo "Exit-status of [ hello ] is:" $?
[ abc = abc ]
echo "Exit-status of [ abc = abc ] is:" $?
[ ]
echo "Exit-status of [ ] is:" $?
[ abc = def ]
echo "Exit-status of [ abc = def ] is:" $?
… will output:
Exit-status of [ hello ] is: 0 — because non-empty string is considered true Exit-status of [ abc = abc ] is: 0 — because 'abc' really is same as 'abc' Exit-status of [ ] is: 1 — because empty string is considered false Exit-status of [ abc = def ] is: 1 — because 'abc' really differs from 'def'
However, bash and many other shells really usually don't invoke /bin/[
(or /usr/bin/[
) in these cases, but call built-in command with exactly the same behavior instead (purely for performance reasons). To invoke /bin/[
(not shell built-in surrogate) you need either to explicitly specify its path (e.g. /bin/[ hello ]
; you don't need to prefix ]
with dirname though ☺), or to configure shell not to use a built-in surrogate (for example, enable -n [
in bash).
P. S.: As it was said in other answers, [
is related to test
. But test
, unlike [
, doesn't require ]
as its last argument (and doesn't expect it at all; adding extra ]
to test
arguments can cause it to fail with error message or to return wrong result). The /bin/test
and /bin/[
can resolve to the same file (e.g. one is symlinked; in this case the behavior diversion is probably implemented by analyzing the currently-called command within the test
/[
code itself) or to different files. For test
, shell also usually invokes built-in surrogate, unless path is explicitly specified (/bin/test
) or it's configured not to do so (enable -n test
).
P. P. S.: Unlike test
and [
, modern if
is never a real file. It's part of shell (e.g. bash) syntax: if commandA; then commandB; fi
(newlines can be used instead of semicolons) causes commandB
to be executed if-and-only-if commandA
exited with zero status. This perfectly fits to behavior of test
or [
, allowing to combine them like if [ "$a" = foo ]; then …; fi
(or if test "$a" = foo; then …; fi
— just less readable). However, modern scripts often use [[
instead of test
or [
, which (as the if
) is never a real file, but always a part of shell syntax.
P. P. P. S.: As for man
— never expect man
to have an article on every command in your file-system. Info on some (even "real", file-based) commands may be missing, info on some shell built-ins maybe present not only within an article dedicated to specific shell (that's the place where you most certainly will find info on test
, [
, if
, [[
). Still, many distributions have explicit man
-articles for test
and [
. (About --help
, it's not recognized with test
for obvious reason: it needs to handle quietly cases like a=--help; test "$a"
; on some distributions [ --help
(without closing ]
) still shows help, on some it doesn't.)
Related videos on Youtube
![Bregalad](https://i.stack.imgur.com/8TNp9.png?s=256&g=1)
Bregalad
Updated on September 18, 2022Comments
-
Bregalad almost 2 years
I am always surprised that in the folder
/bin
there is a[
program.Is this what is called when we are doing something like:
if [ something ]
?By calling the
[
program explicitly in a shell it asks for a corresponding]
, and when I provide the closing bracket it seems to do nothing no matter what I insert between the brackets.Needless to say, the usual way about getting help about a program does not work, i.e. neither
man [
nor[ --help
works.-
Ipor Sircer over 7 yearstry
man expr
. -
Sergiy Kolodyazhnyy over 7 years@IporSircer
[
refers totest
command though, notexpr
, so it should beman test
-
Ipor Sircer over 7 yearsoops. sorry! i was tired.
-
Toby Speight over 7 years
man '['
works fine for me - either you forgot to quote[
or you have a different definition of "works". -
Sergiy Kolodyazhnyy over 7 years@TobySpeight See my and Stephane's answers.
[
has OS-specific implementations , which may or may not have a man page -
Olivier Dulac over 7 yearsA few warnings:
[
evaluates its arguments, so you need to have spaces between all of them.[ a=b ]
is not a comparison: it will always be true (it is a single string: "a=b", which is always evaluated to true) And you should limit the number of arguments to 4 (even though recent implementations will allow more... limiting to 4 makes it more portable. For example :[ "a" = "b" ]
already has 4 arguments : "a" "=" "b" and the non-necessary end of test arg : "]" ). If you need more: chain tests with for example:if [ "$Var_a" = "foo" ] && [ "$Var_b" = "bar" ] ; then : do something ; fi
-
muru over 7 years@OlivierDulac a
!
by itself (unescaped and unquoted) won't be replaced, and even better would beif ! [ ...
. All of the bash expansions that use!
include at least on other character -
Olivier Dulac over 7 years@muru: well, I stand corrected! Which is weird because I've seen "!" replacement occur so many times that I now systematically escape it (hence I didn't notice it would work there...). I'm quite sure it happened (for exemple inside double quotes), even with no trailing character, but I may be wrong.
-
Dmitry Grigoryev over 7 years
-
Baard Kopperud over 7 yearsYou should also note that there is also a
[
-builtin inbash
(and possibly other shells as well), and that this may be used instead of/bin/[
. Also there is thetest
-command, which on many systems is a symbolic link to/bin/[
(or vice versa) - but on others is a separate command.
-
-
franklinsijo over 7 years
/usr/bin/
for Amazon Linux as well. -
Sergiy Kolodyazhnyy over 7 years@StéphaneChazelas Thank you. Answer edited accordingly
-
Gilles 'SO- stop being evil' over 7 years“all Bourne-like shells (and fish) have a built-in [ command” That's true in practice in the 21st century, but I'm pretty sure I've used a shell without
[
. I don't remember if it was a Bourne variant or an antique version of ash. -
jlliagre over 7 years@Gilles The Bourne shell implemented both
[
andtest
as builtins with Unix System III. Before that, there was no[
andtest
was only an external binary command. -
Stéphane Chazelas over 7 years@Gilles, the original ash had test builtin (merged with expr), but optionally. According to in-ulm.de/~mascheck/various/ash, BSDs didn't have test builtin until around 2000. I've added "modern".
-
Stéphane Chazelas over 7 yearsNote that
if
was a command up to Unix V6. Unix V7 (1979) came with the Bourne shell (with itsif-then-else-fi
construct) and the newtest
command. -
Adam over 7 years"man [ definitely works for me on mainstream Gnu/Linux distributions" Hmm.. Centos (admittedly 6.8 still) clearly isn't mainstream enough :)
man test
works but notman [
On the other hand my cygwin environment does known aboutman [
! -
Alavi over 7 yearsWhy in the world would you want to do
find . -exec [ f {} ] \; -print
? The commandfind . -type f -print
would do the same thing so much more efficiently... -
Stéphane Chazelas over 7 years@Thisisn'tmyrealname, no, for the equivalent of
-exec [ -f {} ] \;
(check that the file is regular after symlink resolution), you'd need the GNU-specific-xtype f
. Otherfind
implementation have no equivalent (using-L
/-follow
have other undesirable side-effects). -
Stéphane Chazelas over 7 years@Thisisn'tmyrealname. (continued), You can optimise by doing
\( -type f -o -type l -exec [ -f {} ] \; \)
or invokesh
with-exec {} +
to run the[
builtin in a loop, but that's beyond the point of this Q&A. Note that there's no point usingenv [ ... ]
, those are just to give example of when/bin/[
may be executed. -
jlliagre over 7 years@Adam Yes, you are right, that's more recent than I thought although I didn't wrote all mainstream Linux distros, just that it worked for me (i.e. on the ones I tested). That is OL 7.2 (RHEL 7.2) clone and Mint 17.1 (Ubuntu 14.04).
-
Darkhogg over 7 years
man [
doesn't seem to work on Manjaro (Arch Linux based). The manual fortest
lists[ --help
as a valid invocation that should show help, but interestingly it doesn't, and instead complains about a missing]
, same as with--version
. Adding the]
doesn't do anything, so it seems impossible to get help other than fromman test
. -
jlliagre over 7 years@Darkhogg You can try
/usr/bin/[ --help
or/bin/[ --help
. -
Darkhogg over 7 years@jlliagre You are completely right, I was using the builtins.
env [ --help
as well as/usr/bin/[ --help
work completely fine (although I need to quote/usr/bin/[
or zsh complains).