Some commands are provided as both builtins and external utilities. Take echo for example. On my machine (macOS) running Bash 3.2,

$ type echo
echo is a shell builtin

Running man bash | less --pattern='^ *echo +\[' shows:

echo [-neE] [arg ...]

But running man 1 echo shows a man page for a different implementation of echo, with a different signature:

echo [-n] [string ...]

I'm able to use -e successfully, so I must be running the builtin, and presumably that's /bin/echo

$ which echo

Where does the other implementation live, and how can I distinguish between builtins and external utils in general (e.g. printf)

Update/Correction Thanks @Gilles for clarifying. And the proof is in the pudding!

$ /bin/echo -e "\tabc"
-e \tabc

$ echo -e "\tabc"

2 Answers 2


To find out whether a command is built in, run type.

$ type echo
echo is a shell builtin

type is itself a builtin and knows what commands are built in. (In bash, builtins can be disabled, and type will correctly report that a command is not built in if the builtin has been disabled.) type reports whatever will be executed if you use the command name — alias, function, builtin or external command.

which is an external command that reports the location of external commands. It doesn't know anything about aliases, functions or builtins. And it might even not report the correct external commands, depending on your setup. Just forget about which and use type instead.

I must be running the builtin, and presumably that's /bin/echo

No! By definition, a builtin is not an external command. The code that implements the echo builtin, like all other builtins, is in /bin/bash. /bin/echo is an external command that has the same name as the echo builtin.

When a command exists both as a builtin and as an external command, using its name calls the builtin. The precedence order for command names is alias, then function, then builtin, then external command in the directories listed in $PATH in order. If, for some reason, you want to force an external command, use its full path.

  • Right you are! Thanks for the clarification
    – ivan
    Jun 17, 2017 at 23:14

known name (word)

The best way to find what provides a word (provided that word is a valid name for an alias, function or command) is to use the option -a to type:

$ type -a echo
echo is a shell builtin
echo is /bin/echo

If a function and an alias are also defined, you may get a similar list to this:

$ type -a echo
echo is aliased to `echo "A new echo"'
echo is a function
echo ()
    printf '%s\n' "A function echo" "$@"
echo is a shell builtin
echo is /bin/echo

The order in which they are printed is the priority order. In the list printed above: the alias will be executed first. If the alias is removed (unalias), the function is executed. And so on and so forth.


$ echo "test"
A function echo
A new echo

Quoting usually bypass aliases:

$ \echo         # or "e"cho, "echo", 'e'"ch"o, and many other variations.
A function echo

Which is equal to unsetting the alias:

$ unalias echo; echo test
A function echo

The function may be erassed with unset (option -f) :

$ unset -f echo 
$ type -a echo
echo is a shell builtin
echo is /bin/echo

An alias could be turn off with enable:

$ enable -n echo
$ type -a echo
echo is /bin/echo

And an external (external to the shell) utility may be moved:

# mv /bin/echo /bin/echo-aside
# type -a echo
bash: type: echo: not found

A list of builtins

If the name of the builtin is not known, it could be listed.

In bash, there is an (maybe?) oddly named command called enable (builtin in ksh).
Calling enable with no option will print an enabled list of builtins:

$ enable
enable .
enable :
enable [
enable alias

There are options to print all (-a), only enabled (-p or nothing), and special (as defined by Posix) builtins (-s).

Removing the word enable and making it a one line list:

$ echo $(enable -s | cut -d" " -f2)
. : break continue eval exec exit export readonly return set shift source times trap unset

$ echo $(enable -p | cut -d" " -f2)
. : [ alias bg bind break builtin caller cd command compgen complete compopt continue declare dirs disown echo enable eval exec exit export false fc fg getopts hash help history jobs kill let local logout mapfile popd printf pushd pwd read readarray readonly return set shift shopt source suspend test times trap true type typeset ulimit umask unalias unset wait

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .