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..
*/
The autoconf manual has a section on
portable shell programming.
Although that's not specifically targeting POSIX, it's probably the
most complete collection of what to do and not to do when attempting
to write portable shell code.
Best Answer
This is an "as if" rule.
Simply put: The behaviour of the shell as users see it should not change if an implementation decides to make a standard external command also available as shell built-in.
The contrast that I showed at https://unix.stackexchange.com/a/496291/5132 between the behaviours of (on the one hand) the PD Korn, MirBSD Korn, and Heirloom Bourne shells; (on the other hand) the Z, 93 Korn, Bourne Again, and Debian Almquist shells; and (on the gripping hand) the Watanabe shell highlights this.
For the shells that do not have
printf
as a built-in, removing/usr/bin
fromPATH
makes an invocation ofprintf
stop working. The POSIX conformant behaviour, exhibited by the Watanabe shell in its conformant mode, causes the same result. The behaviour of the shell that has aprintf
built-in is as if it were invoking an external command.Whereas the behaviour of all of the non-conformant shells does not alter if
/usr/bin
is removed fromPATH
, and they do not behave as if they were invoking an external command.What the standard is trying to guarantee to you is that shells can build-in all sorts of normally external commands (or implement them as its own shell functions), and you'll still get the same behaviour from the built-ins as you did with the external commands if you adjust
PATH
to stop the commands from being found.PATH
remains your tool for selecting and controlling what commands you can invoke.(As explained at https://unix.stackexchange.com/a/448799/5132, years ago people chose the personality of their Unix by changing what was on
PATH
.)One might opine that making the command always work irrespective of whether it can be found on
PATH
is in fact the point of making normally external commands built-in. (It's why my nosh toolset just gained a built-inprintenv
command in version 1.38, in fact. Although this is not a shell.)But the standard is giving you the guarantee that you'll see the same behaviour for regular external commands that are not on
PATH
from the shell as you will see from other non-shell programs invoking theexecvpe()
function, and the shell will not magically be able to run (apparently) ordinary external commands that other programs cannot find with the samePATH
. Everything works self-consistently from the user's perspective, andPATH
is the tool for controlling how it works.Further reading