这可能是别名扩展时的问题。bash 手册页有以下内容:
关于别名的定义和使用的规则有些混乱。在执行该行的任何命令之前,Bash 总是读取至少一个完整的输入行。别名在读取命令时扩展,而不是在执行命令时扩展。...
手册页中有更多信息,但是当执行复合语句(例如while
循环中的列表)时,扩展将在第一次读取输入时发生,而不是在执行时发生。由于GIT
在读取输入时未定义,因此会出现错误。
以下代码段显示了该问题:
#!/bin/bash
shopt -s expand_aliases
while read x; do
a=`echo $x | tr '[:lower:]' '[:upper:]'`
alias $a=$x
GIT # <--- this fails to execute git
done < <(echo 'git')
GIT # <--- this executes git
一旦执行了 while 循环,别名就会变得可用。手册页中的详细信息有点粗略,但似乎函数中定义的别名的注释将适用。
此外,在使用诸如echo foo | while read x; do stuff; done
. 循环将while
在子shell中执行,在子shell中所做的任何更改都不会持续到while
循环之外。上面的示例显示了一种方法,以便 while 循环在当前 shell 中执行,但从执行的命令中获取其输入。
更新:
如果上面的脚本以原始形式编写,它将如下所示:
#!/bin/bash
shopt -s expand_aliases
#Currently just using 'git' to try and get it working
#It'll be replaced with the following line when it works
#compgen -c | while read x;
echo 'git' | while read x; do
a=`echo $x | tr '[:lower:]' '[:upper:]'`
alias $a=$x
GIT # <--- this fails to execute git
done
GIT # <--- this also fails executes git because the alias' were set in a subshell.
在这种情况下,别名永远不会被执行。这样做的原因是管道|
星一个新的子外壳并将进程链接在一起。在echo
一个进程中运行,在另一个进程中while read ...
运行,通过管道连接。设置别名时,它们是在运行while
循环的子外壳进程的环境中设置的。但是,当while
循环完成时,该进程终止并丢弃其环境,以及对其环境所做的任何更改,例如添加别名。结果,第二次尝试执行GIT
将失败,因为别名信息已被丢弃。
当您使用第一个脚本中显示的拓扑时,while
循环在当前脚本中运行,因此对环境所做的任何更改都会在循环结束后持续存在。
如果您执行脚本而不是采购它,您也会注意到这种行为。如果您希望能够在这样的脚本中配置一组别名,请运行脚本,让它们在您的环境中可用,您会遇到同样的问题,因为您的脚本在父 shell 的单独进程中运行,创建别名,随后在脚本退出时被丢弃。
$ ./create_aliases
$ alias # <--- Ooops, no aliases defined.
您必须像这样获取脚本,它将在当前进程中运行脚本的内容,从而修改当前环境。
$ source ./create_aliases
$ alias # <--- Yay, new aliases present.
这就是为什么 bash 将源文件如~/.bashrc
,以便您在该文件中所做的更改将存在于当前环境中。