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 is cd
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 mentions printf
, echo
, and test
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 that echo
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 find echo
in the builtin command table, which is in /usr/src/cmd/sh/msg.c
. Based on the timestamps in that file, that proves that echo
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:
/*
builtin commands are those that Bourne did not intend
to be part of his shell.
Redirection of i/o, or rather the lack of it, is still a
problem..
*/
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
You can use the
command
shell built-in to bypass the normal lookup process and run the given command as an external command regardless of any other possibilities (shell built-ins, aliases, etc.). This is often done in scripts which need to be portable across systems, although probably more commonly using the shorthand\
(as in\rm
rather thancommand rm
orrm
, as especially the latter may be aliased to something not known likerm -i
).This can be used with an alias, like so:
The advantage of this over e.g.
alias time=/usr/bin/time
is that you aren't specifying the full path to thetime
binary, but instead falling back to the usual path search mechanism.The
alias
command itself can go into e.g. ~/.bashrc or /etc/bash.bashrc (the latter is global for all users on the system).For the opposite case (forcing use of the shell built-in in case there's an alias defined), you'd use something like
builtin time
, which again overrides the usual search process and runs the named shell built-in. The bash man page mentions that this is often used in order to provide customcd
functionality with a function namedcd
, which in turn uses the builtincd
to do the real thing.