22

So there I was. Trying to append to $PATH. Uh oh! I accidentally unset $PATH! For whatever reason I ran

$ ls
bash: ls: command not found

To be expected. Then I ran

$ echo $PATH
$PATH:/home/jon/.local/bin

Is echo some sort of special case? Why isn't it on $PATH? Is it built in to bash?

Jon
  • 347
  • 2
  • 5
  • 12
    Welcome to [ubuntu.se]! _“I accidentally unset `$PATH`!”_ Not really, you seem to have set it to a not wanted value. Since your PATH now contains `$PATH` literally, you must have used single quotes (`'`) instead of double quotes (`"`): `export PATH='$PATH:/home/jon/.local/bin'`, haven’t you? – Melebius Feb 05 '20 at 11:15
  • Related: [Why is there a /bin/echo and why would I want to use it?](https://askubuntu.com/questions/960822) – Kulfy Feb 07 '20 at 04:15

3 Answers3

62

echo is a bash builtin. It does not use the $PATH to find the echo program, instead bash has it's own version of echo which is used instead of the echo program located in your $PATH

read more here: Bash Builtins (Bash Reference Manual)

Minijack
  • 666
  • 6
  • 7
  • 2
    There's also `/bin/echo`, which would have vanished when OP unset `$PATH` – waltinator Feb 05 '20 at 03:56
  • 13
    @waltinator except, if you call it as `/bin/echo` it would still work regardless of `PATH`, and if you call it as `echo`, you get the builtin and not `/bin/echo` :) – hobbs Feb 06 '20 at 04:39
25

In addition to what Minijack mentioned, you can check what a command is by using the type builtin.

$ type echo
echo is a shell builtin

On the other hand, which can be used to check for executables specifically. Once you unset $PATH, you'll get something like

$ which echo
/usr/bin/which: no echo in ((null))

Whereas with your path set you get

/usr/bin/echo

You can check man builtins for list and desription of various builtins. For example, [ and test are also builtins.

EDIT: which works for me even without PATH because of an alias that uses an absolute path

Mr Redstoner
  • 351
  • 2
  • 7
  • 7
    Without `PATH`, even `which` wouldn't be found, right? – Angew is no longer proud of SO Feb 05 '20 at 15:32
  • @AngewisnolongerproudofSO and without `PATH`, also `man builtins` wouldn't work :-) – andreee Feb 05 '20 at 15:53
  • @AngewisnolongerproudofSO for me which works without path as it's apparently aliased to `alias|/usr/bin/which --tty-only --read-alias --show-dot --show-tilde` i.e. an absolute path. man is not, so @andreee is indeed correct that doesn't work – Mr Redstoner Feb 05 '20 at 15:59
  • 4
    There's no reason to ever use `which`. If you want to know about executables, use [`type`](https://www.gnu.org/software/bash/manual/html_node/Bash-Builtins.html#index-type) with `-a`, `-P`, or `-p`. – wjandrea Feb 05 '20 at 21:47
  • 1
    @wjandrea what's wrong with which? – vikarjramun Feb 06 '20 at 17:40
  • @vikarjramun It's not a builtin, so in this case where OP's PATH is messed up, it's inaccessible (or at least it can't be looked up), but `type` will still work with what's left of the PATH, i.e. `/home/jon/.local/bin`. As well, `type` is more informative in general, e.g. it knows about hashed paths but `which` doesn't. – wjandrea Feb 06 '20 at 18:23
  • 1
    By default `type` simply returns the first hit. If you want to see all of them, use `type -a echo` which will include the one found at `/usr/bin`. The difference between `type` and `which` is that `which` _only_ looks at directories in your `$PATH`, while `type` will look everywhere, including shell builtins, functions etc. As a general rule, [`type` is always preferred over `which`](https://unix.stackexchange.com/questions/85249/why-not-use-which-what-to-use-then/85250?r=SearchResults&s=9|0.0000#85250), for this reason among others. – terdon Feb 07 '20 at 12:12
3

The model employed by the Single Unix Specification (a.k.a. IEEE 1003.1) is that whether a command is built into a shell is a mere optimization, and the behaviour (if invoked in a conformant manner) should be the same for a built-in version of a command as for an external version. (This is regular built-ins, that is. Special built-ins are another matter.) In particular, if the command is not found as an external in a PATH search, the built-in version is not to be executed.

This is indeed what happens with one shell. You'll find that the Watanabe shell conforms to the SUS. In its posixly-correct mode, if echo is not on the path, the built-in echo command in the Watanabe shell will not be executed.

But the 93 Korn, Debian Almquist, Z, and Bourne Again shells in their most conformant modes still all execute built-ins even if there is no corresponding executable on PATH. That is what is happening here for you. The Bourne Again shell has a built-in echo command, and several others besides. It is executing that, even though it has not found an external echo command in a PATH search.

(Note that there are quite a few ways to invoke echo in a non-conformant manner, or at least in a manner where the result is unspecified: Using -n; using a backslash in any argument; using -e; using other things expecting them to be command options or end of options markers. These not only reveal whether it is a built-in echo, but even to an extent reveal what shell is in use. Fortunately, you did not hit any of them. ☺)

Further reading

JdeBP
  • 3,931
  • 24
  • 38
  • 1
    I suspect the reason a lot of shells stil invoke a builtin even if the exacutable is present is the same they have it as a builtin to begin with, speed. Searching for the exacutable wastes time, and they have those builtins specifically to save time. – Mr Redstoner Feb 08 '20 at 09:01