A shell knows four kinds of commands.
- Aliases: these are nicknames for a command with some options. They are defined in the shell's initialization file (
~/.bashrc
for bash).
- Functions: they are snippets of shell code given a name. Like aliases, they are defined in the shell's initialization file.
- Builtins: the shell comes with a small number of built-in commands. Most builtins manipulate the shell state (
cd
changes the current directory, set
changes options and positional parameters, export
changes the environment, …). Most shell offer largely the same builtins but each shell has a few extensions to the basic set.
- External commands: they are independent of the shell. Like other programs, the shell executes external programs by looking them up in the executable search path. The
PATH
environment variable contains a colon-separated list of directories to search for programs.
In case there are commands of several types by the same name, the first match in the order above is executed¹.
You can see what type of command a name corresponds to by running type some_name
.
You can list aliases by running the alias
built-in with no argument. There is no way to list functions or builtins that works in all shells. You can find a list of builtins in the shell's documentation.
In bash, the set
builtin lists functions with their definitions as well as variables. In bash, ksh or zsh, typeset -f
lists functions with their definitions. In bash, you can list all command names of any type with compgen -c
. You can use compgen -A alias
, compgen -A builtin
compgen -A function
to list commands of a specific type. You can pass an additional string to compgen
to list only commands that start with that prefix.
In zsh, you can list the currently available commands of a given type with echo ${(k)aliases}
, echo ${(k)functions}
, echo ${(k)builtins}
and echo ${(k)commands}
(that last one lists external commands only).
The following shell-agnostic snippet lists all available external programs:
case "$PATH" in
(*[!:]:) PATH="$PATH:" ;;
esac
set -f; IFS=:
for dir in $PATH; do
set +f
[ -z "$dir" ] && dir="."
for file in "$dir"/*; do
if [ -x "$file" ] && ! [ -d "$file" ]; then
printf '%s = %s\n' "${file##*/}" "$file"
fi
done
done
There is an edge case in Bash: hashed commands.
Bash Reference Manual says:
A full search of the directories in $PATH is performed only if the command is not found in the hash table
Try:
set -h
mkdir ~/dir-for-wat-command
echo 'echo WAT!' >~/dir-for-wat-command/wat
chmod +x ~/dir-for-wat-command/wat
hash -p ~/dir-for-wat-command/wat wat
wat
The PATH
environment variable doesn't contain ~/dir-for-wat-command
, compgen -c
doesn't show wat
, but you can run wat
.
If you want to shadow an existing command, define an alias or a function.
¹ Exception: a few builtins (called special builtins) cannot be shadowed by a function — bash and zsh don't comply with POSIX on that point in their default mode though.