43
$ which echo
echo: shell built-in command.
$ which ls
/bin/ls
$ which cat
/bin/cat

Why is echo not an independent utility like ls, ps, cat etc? Why is it shell specific? Any good reasons?

Lazer
  • 35,307
  • 6
    Bear in mind this is shell-specific. ZSH builds it in, BASH does not. – tsvallender Aug 29 '10 at 10:11
  • 24
    @tsv: echo most certainly is a Bash builtin. In fact, it's a builtin in every major modern shell. – Dennis Williamson Aug 29 '10 at 20:19
  • which echo (with MacOS Ventura_13.4): Under zsh returns echo: shell built-in command; Under bash returns /bin/echo. type echo (with MacOS Ventura_13.4): Under zsh returns echo is a shell builtin; Under bash returns echo is a shell builtin. – jubilatious1 Jun 20 '23 at 17:34

6 Answers6

77

There are two classes of builtins:

  1. 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?)

  2. 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..
*/      
Warren Young
  • 72,032
  • On page 385 of the SysV UM, Vol II you mentioned, there is the builtin print command for ksh which is said to behave like echo. This command is present in something like AT&T Unix SVR4 2.1 i386 from ISC too. print "\012" yields the same result as would echo. Wonder why ksh had print and not echo built-in? Anyways really interesting. –  Dec 31 '13 at 01:10
  • How many such gems from a few years ago are snuck away waiting to be unearthed by an edit in 2016? +1 – iruvar Mar 05 '16 at 09:16
  • +1. Thanks. Could you provide the sources (references) for the purpose of making a command builtin, for my (systematic) reading/learning? – Tim Mar 16 '16 at 09:08
  • I want to find some reference, manual or standard to systematically read or learn. I tried to look it up in the bash reference manual, and POSIX specifications. But I can't find it. I am not sure if I miss them. – Tim Mar 16 '16 at 14:49
  • @Tim: As this and other answers here explain, some commands must be built into the shell or they cannot do their work. All the others are purely optional. That's essentially what my Bourne shell source snippet says above, in fact. Since building such commands into the shell is optional, an authoritative rationale could only give one implementer's opinion. – Warren Young Mar 16 '16 at 15:16
24

There is a third reason for some commands to be built-in: They can be used when running external commands is impossible.

Sometimes a system becomes so broken that the ls command does not work. In some cases, an echo * will still work.

Another (more important!) example is kill: If a system runs out of free PIDs, it is not possible to run /bin/kill (because it needs a PID :-), but the built-in kill will work.

Btw., which is an external command (at least it is not internal in bash), so it cannot list internal commands. For instance:

$ which echo
/bin/echo
$ type -a echo
echo is a shell builtin
echo is /bin/echo
bhm
  • 751
  • Don't forget that almost all external executables rely on library files. Sometimes catastrophes happen and those libraries can't be loaded (advice: NEVER issue ldconfig unless you know EXACTLY what you are doing). Also, what if you blow away your /bin, or worse, your /sbin partition, but still have a shell loaded? – LawrenceC Jan 03 '11 at 16:09
  • If you run out of PIDs it can be difficult to find out which PID you want to kill. You cannot run a ps command, so you would have to find the PIDs manually through /proc. – kasperd Nov 25 '15 at 14:13
  • Although on (at least) CentOS which is a bash alias that runs alias | /usr/bin/which --read-alias ... so the external which can identify aliases (but still not builtins). I can't decide whether to consider this brilliant or perverted. – dave_thompson_085 May 26 '16 at 01:41
  • At the point that ls command doesn't work anymore, I think you should be mounting the drive as slave instead of boot/master and fixing the file structure that way. I'm not sure how you could fix that extent of system corruption with echo unless you are echo > into a file and that would take a long time to fix. Granted you could write a bash script that was to fix the system structure. – Root James Aug 04 '18 at 01:07
16

According to the Bash Reference Manual, it's about convenience.

Shells also provide a small set of built-in commands (builtins) implementing functionality impossible or inconvenient to obtain via separate utilities. For example, cd, break, continue, and exec) cannot be implemented outside of the shell because they directly manipulate the shell itself. The history, getopts, kill, or pwd builtins, among others, could be implemented in separate utilities, but they are more convenient to use as builtin commands. All of the shell builtins are described in subsequent sections.

The Advanced Bash Scripting Guide has a more detailed explanation:

"A builtin is a command contained within the Bash tool set, literally built in. This is either for performance reasons -- builtins execute faster than external commands, which usually require forking off 1 a separate process -- or because a particular builtin needs direct access to the shell internals."

Also note that echo does exist as a standalone utility on some systems. Here's what I have on my Darwin system (MacOSX 10.5.8 - Leopard)

$ uname -a
Darwin host.foo.org 9.8.0 Darwin Kernel Version 9.8.0: Wed Jul 15 16:55:01 PDT 2009; root:xnu-1228.15.4~1/RELEASE_I386 i386
$ bash --version
GNU bash, version 3.2.17(1)-release (i386-apple-darwin9.0)
Copyright (C) 2005 Free Software Foundation, Inc.
$ which echo
/bin/echo

echo is also available as a builtin, but apparently my scripts use /bin/echo on my Mac, and use a Bash builtin on most of my Linux & FreeBSD systems. But that doesn't seem to matter, because the scripts still work fine everywhere.

Stefan Lasiewski
  • 19,754
  • 24
  • 70
  • 85
7

To complement bhm's answer, let's say /bin was accidentally removed from your PATH. You'd want to be able to echo $PATH to find that out, right?

3

Here is the real reason why echo should be a shell builtin:

Suppose you have a password in $PASSWORD. How do you write it to a file ./password? Naturally most programmers would write:

echo "$PASSWORD" >./password

However, if echo were not a shell builtin, the password would leak to all users through ps information.

Of course, if you want to be clever about it, you can find a way to store a password without echo, perhaps exploiting some other shell feature:

cat >./password <<EOF
${PASSWORD}
EOF

However, having echo as a builtin is an important seatbelt as the most obvious way to save a password to a file should work too.

  • 3
    While a useful side effect I find it highly doubtful that this was the reason. – plugwash Jun 17 '17 at 23:44
  • @DepressedDaniel: What does back you up? Where is the reference you used to support your answer? – joker Apr 14 '18 at 19:26
  • 1
    Well, writing confidential information to files or (via a pipe) to commands is certainly a good reason to have an echo builtin. But being able to still evaluate things on an otherwise "fully loaded" system or just performance of scripts are surely equally valid reasons. – Kai Petzke Jul 31 '21 at 15:03
2

Although most shells include a built-in echo nowadays, the GNU CoreUtils also include a standalone implementation of it:

$ which echo
/bin/echo
$ dpkg -S /bin/echo
coreutils: /bin/echo

It sounds like you don't have GNU Coreutils installed (most linux-based desktop & server OS have it installed by default, but embeded linux or other UNIX might use alternative collections of shell utilities instead).

BTW: if you look at Busybox, you'll see that ls, ps and cat are also built-in commands there (or at least can be; it's used for embeded systems and everything not needed can be left out).

JanC
  • 1,399
  • 4
    The original poster's tests don't support the hypothesis that /bin/echo doesn't exist. They just show that the builtin takes precedence over any echo program in the PATH. – Warren Young Aug 30 '10 at 14:54