10

If I execute the test command in bash, test(evaluates conditional expression) built-in utility is started:

$ type test
test is a shell builtin
$ type -a test
test is a shell builtin
test is /usr/local/bin/test
test is /usr/bin/test
$ 

However, as seen in output of type -a test above, there is another test in /usr/local/bin directory and yet another one in /usr/bin directory. How are executables ordered, i.e. are the built-in commands always preferred and then the rest of the commands depend on the directory order in $PATH variable? In addition, is it possible to change the order of the executables started, e.g. if I type in test, then /usr/bin/test is started instead of bash-builtin test?

  • You can specify the full path when calling the command, eg., /usr/bin/test -f "$file"... – jasonwryan May 29 '14 at 22:43
  • @jasonwryan I'm aware of this, but I'm just interested if there is a way to change the order of executables started. – Martin May 29 '14 at 22:48
20

Highest priority is bash alias, then special builtins (only in POSIX mode), then functions, then builtins, then a search in $PATH.

To execute a builtin, use builtin test.
To execute an external application, use an explicit path: /bin/test.
To ignore functions and aliases, use command test.
To bypass just alias, use \test or any other kind of expansion.

It's possible to disable/enable a builtin with enable test.

(Updated according to comments below)
(Fixed incorrect admin edit that bash has disable builtin - in fact, there is only enable)

  • 1
    @1_CR gena2x is right. My answer omitted special builtins, which take precedence over functions as per POSIX (though some shells are not compliant; bash complies only in POSIX mode). – Gilles May 29 '14 at 23:55
  • 1
    Suggested edit: Aliases are disabled when you quote the command (or any part of it), as in \test or 'test' or tes't'. – John Kugelman May 30 '14 at 12:58
  • 2
    That's not full picture. Seems any kind of expansion (in bash manual, all the substitution, tilde expansion and so on called expansion) disables aliases. I tried. – gena2x May 30 '14 at 13:05
  • 1
    Quote from the bash man page: "The first word of each simple command, if unquoted, is checked to see if it has an alias. If so, that word is replaced by the text of the alias. The characters /, $, backtick, and = and any of the shell metacharacters or quoting characters listed above may not appear in an alias name." – John Kugelman May 30 '14 at 15:02
  • 1
    My statement says that any kind of expansion prevents alias substitution. This comply to the man page which says that any word which contains no symbols out of restricted set will never been substituted. Your statement that unquoted words prevent alias substitution. Both statements comply to bash manual. My statement is just broader than your and still holds truth. If you think that my statement is not true, please provide an example of the expansion which doesn't prevent alias substitution. – gena2x May 30 '14 at 16:34
5

Built-in commands are always preferred to external commands. The rationale is that the built-in command is faster (and in a few cases, such as cd or test -o BASH_OPTION, only the built-in command can have the desired effect).

Sometimes the external command may have capabilities that the shell builtin doesn't. In that case, you can call the external command by giving an explicit path (i.e. containing a slash) (this bypasses any concern about the order in $PATH). If you don't want to hard-code the external path but you do want to prevent the use of the builtin, you can use "$(type -P test)" (note capital P) in bash, "$(whence -p test)" in ksh, and =test in zsh. Another way to force the use of an external command is to use the command builtin (command -p test …) or to go through the env utility (env test …).

In zsh, you can disable a builtin with disable test. This is permanent (for the current shell or subshell) until the builtin is reenabled with enable test. In bash, you can do the same with enable -n test to disable and enable test to reenable.

You can use an alias or function to force the execution of a different command, for example alias test=/usr/bin/test or test () { /usr/bin/test "$@"; }. If you have such an alias, you can prevent its use by quoting any part of it, e.g. \test will perform the normal function/builtin/external lookup. Note that depending on the shell and its settings, alias definitions in a function may be expanded when a function is read or when it is executed. If you've defined a function, you can use command test to prevent function lookup as well as alias lookup (so here the test builtin will be invoked unless disabled).

  • wouldnt env be appropriate here too? – Steven Penny Jan 23 '17 at 0:58
  • so, if the shell runs from BusyBox, are other, usually external commands from the same BusyBox considered as internals? e.g. I added full df to a PATH on first position, removed alias 'df', which df shows /opt/bin/df, but df runs /bin/df -> busybox – papo Dec 24 '18 at 11:02
  • @papo which df does not necessarily show you what df runs. unix.stackexchange.com/questions/85249/… – Gilles Dec 24 '18 at 14:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.