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.