Bash 在别名、函数、二进制文件中的搜索顺序

Bash search order among aliases, functions, binaries

function mycmd { echo 'function'; }

# After defining the alias, the function stops being visible, even if redefined.
alias mycmd="echo 'alias'"

# Yet if a binary is found in $PATH, it hides both.
echo "#!/usr/bin/env bash" > mycmd
echo "echo 'file'" >> mycmd
chmod +x mycmd
export PATH=$(pwd):"$PATH"

这些观察是否正确可靠?这种影子规则的预期好处是什么?

据我所知man bash中没有任何地方说明优先级规则,但手册中的类型列表是根据优先级:

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. […]

重要的部分是:

alias, shell reserved word, function, builtin, or disk file

否 — 关于 PATH 上 script/binary 的观察是不正确的。

当别名生效时,脚本和函数实际上都是不可见的。请参阅 Bash 手册中的 Command search and execution and Aliases

基本上,简单命令名称(名称中没有斜线)的顺序是,从头到尾:

  • 别名
  • 函数
  • built-ins
  • 外部可执行文件(通过 PATH 找到)

你可以用你的脚本改编来演示:

#!/bin/bash

function mycmd { echo "function: $*"; }

mycmd should be the function

# After defining the alias, the function stops being visible, even if redefined.
alias mycmd="echo 'alias'"

mycmd should be the alias

# Yet if a binary is found in $PATH, it hides both.
echo "#!/usr/bin/env bash" > mycmd
echo 'echo "file: $*"' >> mycmd
chmod +x mycmd
export PATH=$(pwd):"$PATH"

mycmd should still be the alias

unalias mycmd

mycmd should be the function once more

unset -f mycmd

mycmd should finally be the file

我已经修改了脚本和函数以回显命令的任何参数(并且在这种情况下使用 $* 而不是 "$@" 是合适的,尽管肯定可以编写代码以使用 "$@" 代替)。

示例输出(脚本 so43.sh):

$ bash so43.sh
function: should be the function
function: should be the alias
function: should still be the alias
function: should be the function once more
file: should finally be the file
$

为什么是这个函数?好问题!默认情况下,别名在脚本中不起作用。他们确实以交互方式工作。当我复制我的脚本(减去 shebang)并将其粘贴到我的终端时,我得到了这个输出(其中 Scafell-Pike JL: 是我的提示):

Scafell-Pike JL:     function mycmd { echo "function: $*"; }
Scafell-Pike JL: 
Scafell-Pike JL:     mycmd should be the function
function: should be the function
Scafell-Pike JL: 
Scafell-Pike JL:     # After defining the alias, the function stops being visible, even if redefined.
Scafell-Pike JL:     alias mycmd="echo 'alias'"
Scafell-Pike JL: 
Scafell-Pike JL:     mycmd should be the alias
alias should be the alias
Scafell-Pike JL: 
Scafell-Pike JL:     # Yet if a binary is found in $PATH, it hides both.
Scafell-Pike JL:     echo "#!/usr/bin/env bash" > mycmd
Scafell-Pike JL:     echo 'echo "file: $*"' >> mycmd
Scafell-Pike JL:     chmod +x mycmd
Scafell-Pike JL:     export PATH=$(pwd):"$PATH"
Scafell-Pike JL: 
Scafell-Pike JL:     mycmd should still be the alias
alias should still be the alias
Scafell-Pike JL: 
Scafell-Pike JL:     unalias mycmd
Scafell-Pike JL: 
Scafell-Pike JL:     mycmd should be the function once more
function: should be the function once more
Scafell-Pike JL: 
Scafell-Pike JL:     unset -f mycmd
Scafell-Pike JL: 
Scafell-Pike JL:     mycmd should finally be the file
file: should finally be the file
Scafell-Pike JL:

现在您可以看到别名在起作用了。

手册中关于别名的部分(在顶部附近引用)说:

Aliases are not expanded when the shell is not interactive, unless the expand_aliases shell option is set using shopt (see The Shopt Builtin).

在脚本顶部添加 shopt -s expand_aliases 并重新运行产量:

$ bash so43.sh
function: should be the function
alias should be the alias
alias should still be the alias
function: should be the function once more
file: should finally be the file
$