Eliah's done a great job of answering this, but I want to comment about the "why is there another version of
echo separate from the Bash program" part. That's the wrong question.
The right question is: why is this a builtin in the first place, when it could have been (and is) a perfectly fine external command?
For simplicity, take a look at the builtins in dash, a measly 38 (bash has 61, for comparison, going by the output of
. continue getopts readonly type
: echo hash return ulimit
[ eval jobs set umask
alias exec kill shift unalias
bg exit local test unset
break export printf times wait
cd false pwd trap
command fg read true
How many of these need to be builtins?
true don't need to be builtins: They don't do anything that only a builtin can do (affect or obtain shell state that's not available to external commands). Bash's
printf at least takes advantage of being a builtin:
printf -v var saves the output to the variable
time in bash is also special: by being a keyword, you can time arbitrary command lists in bash (dash doesn't have a
pwd doesn't need to be a builtin either - any external command is going to inherit the current working directory (and it's an external command too).
: is an exception - you need a NOP, and
: is it. The rest do actions that an external command can easily do.
So, a fifth of these builtins don't need to be builtins. Why, then? The
dash manpage* actually explains in passing why these are builtins (emphasis mine):
This section lists the builtin commands which are builtin because they
need to perform some operation that can't be performed by a separate
process. In addition to these, there are several other commands that may
be builtin for efficiency (e.g. printf(1), echo(1), test(1), etc).
That's pretty much it: these builtins are there because they're used so often, interactively and in scripts, and their functionality is simple enough, that the shell can do the job. And so it happens: some (most?) shells took on the job.** Go back to the
sh from 2.9 BSD, and you won't find an
So, it's entirely possible a minimal shell can skip implementing such commands as builtins (I don't think any current shell does, though). The GNU coreutils project doesn't assume that you're going to run them in a particular shell, and POSIX requires these commands. So, coreutils provides these anyway, and skips those which don't have any meaning outside of the shell.
* This is almost identical to the corresponding manpage text for the Almquist shell, which is what dash, the Debian Almquist shell, is based on.
zsh takes this idea to the extreme: the commands you get by loading various modules, like
zmv, are things you wouldn't think a shell need even get into. At that point, the real question is: why would you use bash instead of zsh, which has all these builtins?