66

What can I type at my shell (which happens to be bash) that will list all the commands that are recognized?

Also, does this differ by shell? Or do all shells just have a "directory" of commands they recognize?

Secondly, different question, but how can I override any of those? In other words how can I write my own view command to replace the one existing on my Ubuntu system, which appears to just load vim.

themirror
  • 6,988

7 Answers7

98

You can use compgen

compgen -c # will list all the commands you could run.

FYI:

compgen -a # will list all the aliases you could run.
compgen -b # will list all the built-ins you could run.
compgen -k # will list all the keywords you could run.
compgen -A function # will list all the functions you could run.
compgen -A function -abck # will list all the above in one go.
Rahul Patil
  • 24,711
  • 11
    Note that compgen -c will include things like { or while that are not strictly speaking commands. – Stéphane Chazelas Jul 28 '15 at 11:41
  • +10 @rahul-patil has given a solid answer, and explained each step along the way. – dank8 May 29 '23 at 05:47
  • 1
    You might want to add that compgen only works for bash. It does not work for any other shell I know; be they sh-compatible (e.g. ash, dash, busybox sh, ksh, mksh) or not (e.g. csh, tcsh, zsh, fish, ...). – Socowi Nov 12 '23 at 16:55
24

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.

  • 1
    Another edge case is for values of $PATH like /bin:/usr/bin: (trailing :), where it would fail to list the executable files in the current directory. – Stéphane Chazelas Jul 28 '15 at 11:45
7

Try this, using :

(   # usage of a sub processus: the modificaion of PATH variable is local in ( )
    PATH+=:EOF # little hack to list last PATH dir
    printf '%s\n' ${PATH//:/\/* }
)
3

The list of commands consists of two sets:

  1. The commands built-in to the shell
  2. The commands in your PATH

You can't change built-ins, but you can keep the shell from using a built-in by specifying the full pathname like /bin/echo.

As for the commands in your PATH, you can change that. PATH is a colon-separated list of directories to look for commands in. The first matching file found "wins".

export PATH=~/bin:${PATH}

(This syntax does not work for csh-derived shells, but all others that I know about).

That exports PATH with ~/bin as the first entry followed by the rest of the existing PATH entries. So now your shell will check directory calledbinin your home directory for a command *first* before checking the standardPATH` the system set up. You could easily make it check the new directory last by instead using:

export PATH=${PATH}:~/bin

To make the PATH persist for future logins, add it to a .profile or other shell rc file.

kurtm
  • 7,055
1

In the bash shell, for the list of aliases, reserved words, functions and external commands in directories of $PATH (assuming none of them contain newline characters) sorted alphabetically:

compgen -ac | sort
ptrbrn
  • 11
1

If you press tab key twice and then y, you will get the list of all available command in your current shell. For the second question I think you should use alias: shell alias.

Vombat
  • 12,884
  • regarding alias, is that a fool-proof way of overriding and un-overriding (by "unaliasing") a command when it is called anywhere on the system (for example from a shell script)? – themirror Oct 12 '13 at 17:39
  • 1
    What shell do you use? I can't get the double tab to list all commands on my bash. – terdon Oct 12 '13 at 17:46
  • I use bash on Ubuntu 13.04. There is also a command called compgen which can be used to get the list of all available commands on the bash. – Vombat Oct 12 '13 at 17:51
  • @terdon Do you have bash completion installed? – Vombat Oct 12 '13 at 17:53
  • Yes I do, but this does not work. Are you sure you are using bash? I tried with different users and also with --norc and it never works for me. Double tab does nothing unless I have started typing something. It might be a shopt option or something. Why don't you add an explanation of how to use compgen for this to your answer? – terdon Oct 12 '13 at 17:56
  • Someone else just did! :P – Vombat Oct 12 '13 at 17:58
  • @terdon Almost all systems I use double-tapping TAB just works. Are you really using bash, or are you using /bin/sh? Since some systems (Debian, Ubuntu) default to dash for /bin/sh, you might be using dash. And with my testing just now, dash does not do the double-tap TAB listing. – kurtm Oct 12 '13 at 18:03
  • @kurtm nope, I'm on bash. Even tried with a bash --norc and with a new user with default settings and double tab does nothing unless I have started typing something. Also tried different terminals. Could you both have set an option somewhere that activates this? Can you confirm that double tab expands to all commands even without having typed anything, just an empty prompt? – terdon Oct 12 '13 at 18:08
  • @terdon What is your bash version? – Vombat Oct 12 '13 at 18:14
  • @terdon I only tried with an empty prompt. And I don't have a .bashrc or any bash-specific options in rc files. – kurtm Oct 12 '13 at 18:15
  • 4.2.45(1) but I must have some weird setting somewhere I guess. It does not work on my laptop (LMDE, basically debian) but does on a SuSe server and on an Ubuntu VM I just tried it in. I'll have to look around and see what that might be. @kurtm – terdon Oct 12 '13 at 18:17
  • 1
    set show-all-if-ambiguous off – Vombat Oct 12 '13 at 18:19
  • Run the above command and then try double tab again. – Vombat Oct 12 '13 at 18:20
0

@Gilles_Quenot's solution without paths, the pure commands:

printf '%s\n' ${PATH//:/\/* } | xargs -n 1 basename

(@Gilles_Quenot sorry, no reputation to comment)
There are more than 3600 commands ( printf '%s\n' ${PATH//://* } | wc ), so a file could be better:

printf '%s\n' ${PATH//:/\/* } | xargs -n 1 basename > commands.txt