The difference between [
and [[
is quite fundamental.
[
is a command. Its arguments are processed just the way any other commands arguments are processed. For example, consider:
[ -z $name ]
The shell will expand $name
and perform both word splitting and filename generation on the result, just as it would for any other command.
As an example, the following will fail:
$ name="here and there"
$ [ -n $name ] && echo not empty
bash: [: too many arguments
To have this work correctly, quotes are necessary:
$ [ -n "$name" ] && echo not empty
not empty
[[
is a shell keyword and its arguments are processed according to special rules. For example, consider:
[[ -z $name ]]
The shell will expand $name
but, unlike any other command, it will perform neither word splitting nor filename generation on the result. For example, the following will succeed despite the spaces embedded in name
:
$ name="here and there"
$ [[ -n $name ]] && echo not empty
not empty
Summary
[
is a command and is subject to the same rules as all other commands that the shell executes.
Because [[
is a keyword, not a command, however, the shell treats it specially and it operates under very different rules.
type
tells you what the shell would use. For example:
$ type echo
echo is a shell builtin
$ type /bin/echo
/bin/echo is /bin/echo
That means that if, at the bash prompt, you type echo
, you will get the built-in. If you specify the path, as in /bin/echo
, you will get the external command.
which
, by contrast is an external program that has no special knowledge of what the shell will do. On debian-like systems, which
is a shell script which searches the PATH for the executable. Thus, it will give you the name of the external executable even if the shell would use a built-in.
If a command is only available as a built-in, which
will return nothing:
$ type help
help is a shell builtin
$ which help
$
Now, let;s look at cat
:
$ type cat
cat is hashed (/bin/cat)
$ which cat
/bin/cat
cat
is an external executable, not a shell builtin.
Best Answer
There are two classes of builtins:
Some commands have to be built into the shell program itself because they cannot work if they are external.
cd
is one such since if it were external, it could only change its own directory; it couldn't affect the current working directory of the shell. (See also: Why iscd
not a program?)The other class of commands are built into the shell purely for efficiency.
The
dash
man page has a section on builtins which mentionsprintf
,echo
, andtest
as examples of commands in this class.Unix systems have always included separate executables for commands in that second class. These separate executables are still available on every Unixy system I've used, even though they're also built into every shell you're likely to use. (POSIX actually requires that these executables be present.)
I believe
echo
got built into the shell in AT&T Unix System V Release 3.1. I base that on comparisons of two different editions of manuals for AT&Ts 3B1 series Unix systems. Someone has kindly scanned 1986 editions of these manuals and put them online; these correspond to the original release of SVR3. You can see thatecho
isn't in the list on page 523 of UNIX System V User's Manual, Volume II, where you'd expect it if the command were built into the shell. In my local paper copy of the SVR3.1 manuals from 1987,echo
is listed in this section of the manual.I'm pretty sure this isn't a Berkeley CSRG innovation that AT&T brought back home. 4.3BSD came out the same year as SVR3, 1986, but if you look at 4.3BSD's sh.1 manpage, you see that
echo
is not in the "Special Commands" section's list of built-in commands. If CSRG did this, that leaves us wanting a documented source to prove it.At this point, you may wonder if
echo
was built into the shell earlier than SVR3.1 and that this fact simply wasn't documented until then. The newest pre-SVR3 AT&T Unix source code available to me is in the PDP-11 System III tarball, wherein you will find the Bourne shell source code. You won't findecho
in the builtin command table, which is in/usr/src/cmd/sh/msg.c
. Based on the timestamps in that file, that proves thatecho
certainly wasn't in the shell in 1980.Trivia
The same directory also contains a file called
builtin.c
which doesn't contain anything on-point for this question, but we do find this interesting comment: