precedence of execution

Commands can take several forms. There can be two or more, of different forms, under the same command name. Find out in what forms the "cd" and "echo" commands exist on your system:

type  -a  cd
type  -a  echo

There is a single cd, but there are 2 manifestations of echo. That is, there are 2 different bodies of executable code on your system that both answer to the name "echo". The rules for precedence of execution determine which one runs when you type an unqualified "echo" on your command line.

Make some additional "cd"s:

alias  cd="echo This is an alias called cd"
function cd() { echo This is a function called cd; }
echo "echo This is a script called cd in /usr/bin/" > /usr/bin/cd;chmod +x /usr/bin/cd
echo "echo This is a script called cd in /bin/" > /bin/cd;chmod +x /bin/cd

The type command (a builtin) can check what cd's exist now. For reference:

type [-aftpP] name [name ...]
 With no options, indicate how each name  would  be  interpreted  if
 used  as  a  command name.  If the -t option is used, type prints a
 string which is one of alias, keyword, function, builtin,  or  file
 if  name  is  an  alias, shell reserved word, function, builtin, or
 disk file, respectively.  If the name is not found, then nothing is
 printed, and an exit status of false is returned.  If the -p option
 is used, type either returns the name of the disk file  that  would
 be executed if name were specified as a command name, or nothing if
 ''type -t name'' would not return file.  The  -P  option  forces  a
 PATH  search  for  each  name,  even  if ''type -t name'' would not
 return file.  If a command is hashed, -p and -P  print  the  hashed
 value, not necessarily the file that appears first in PATH.  If the
 -a option is used, type prints all of the places  that  contain  an
 executable named name.  This includes aliases and functions, if and
 only if the -p option is not also used.  The table of  hashed  com-
 mands  is  not  consulted  when using -a.  The -f option suppresses
 shell function lookup, as with the command builtin.   type  returns
 true if any of the arguments are found, false if none are found.
						--bash man page

So, what cd's are there right now? :

type  -a  cd

Find out which one will run if you type an unqualified "cd" on the command line:

type  cd    and/or    type  -t  cd

Do it:

cd

Is the version of cd that executed the one that predicted by "type"?

Get rid of the alias named "cd":

unalias  cd

Now look at the diminished "stack" of remaining cd's:

type  -a  cd

The alias version isn't on the list anymore. Which version has top precedence now?

type  cd    and/or    type  -t  cd

Try it:

cd

Did "type" predict correctly?

Get rid of the function:

unset  cd

Once again, check the list of cd's, which one is predicted to be chosen, and run to make sure:

type  -a  cd
type  -t  cd
cd

Get rid (temporarily) of the builtin:

enable  -n  cd

Again, test and run:

type  -a  cd
type  cd
cd

There are two cd's, both script files, one in /bin and the other in /usr/bin. Both of those appear in the PATH variable. Check it to see the order in which the 2 containing directories appear:

echo  $PATH

When the shell uses PATH to determine the fully qualified filename for a command you type for the 1st time, it hashes (caches) it so as to avoid a repeat lookup the 2nd time. Void the hash table of any "cd" entry it may contain:

hash  -d  cd

Run cd, and note which of the 2 files gets called:

cd

Examine the hash table entry for cd:

hash  -t  cd

Remove the cd file that was called (and that the hash table references), leaving only the other one.

rm  /bin/cd

Try to run cd again:

cd

Get "/bin/cd" out of the hash table:

hash  -d  cd
hash  -t  cd

See if cd can run now, and if so what runs and what gets cached in the hash table:

cd
hash  -t  cd

Examine the hash table as a whole; it probably will show you some commands other than cd that you have run recently:

hash

To clean up, delete the remaining cd script and restore the cd builtin:

rm  /usr/bin/cd
enable  cd

 

For reference, excerpts from bash man page:

COMMAND EXECUTION
 After  a command has been split into words, if it results in a simple com-
 mand and an optional list of arguments, the following actions are taken.

 If the command name contains no slashes, the shell attempts to locate  it.
 If there exists a shell function by that name, that function is invoked as
 described above in FUNCTIONS.  If the name does not match a function,  the
 shell searches for it in the list of shell builtins.  If a match is found,
 that builtin is invoked.

 If the name is neither a shell function nor a  builtin,  and  contains  no
 slashes, bash searches each element of the PATH for a directory containing
 an executable file by that name.  Bash uses a hash table to  remember  the
 full  pathnames of executable files (see hash under SHELL BUILTIN COMMANDS
 below).  A full search of the directories in PATH is performed only if the
 command  is  not  found in the hash table.  If the search is unsuccessful,
 the shell prints an error message and returns an exit status of 127.

 If the search is successful, or if the command name contains one  or  more
 slashes,  the  shell  executes  the  named program in a separate execution
 environment.  Argument 0 is set to the name given, and the remaining argu-
 ments to the command are set to the arguments given, if any.

 If  this execution fails because the file is not in executable format, and
 the file is not a directory, it is assumed to be a shell  script,  a  file
 containing  shell  commands.   A  subshell is spawned to execute it.  This
 subshell reinitializes itself, so that the effect is as if a new shell had
 been  invoked  to handle the script, with the exception that the locations
 of commands remembered by the parent (see hash below under  SHELL  BUILTIN
 COMMANDS) are retained by the child.

 If  the  program  is  a file beginning with #!, the remainder of the first
 line specifies an interpreter for the program.   The  shell  executes  the
 specified  interpreter  on  operating systems that do not handle this exe-
 cutable format themselves.  The arguments to the interpreter consist of  a
 single  optional argument following the interpreter name on the first line
 of the program, followed by the name of the program, followed by the  com-
 mand arguments, if any.
...
hash [-lr] [-p filename] [-dt] [name]
 For each name, the full file name of the command is  determined  by
 searching  the  directories  in  $PATH  and  remembered.  If the -p
 option is supplied, no path search is performed,  and  filename  is
 used  as  the  full file name of the command.  The -r option causes
 the shell to forget all remembered locations.  The -d option causes
 the  shell  to forget the remembered location of each name.  If the
 -t option is supplied, the full pathname to which each name  corre-
 sponds  is  printed.   If multiple name arguments are supplied with
 -t, the name is printed before the hashed full  pathname.   The  -l
 option causes output to be displayed in a format that may be reused
 as input.  If no arguments are given, or if only  -l  is  supplied,
 information  about remembered commands is printed.  The return sta-
 tus is true unless a name is not found or an invalid option is sup-
 plied.