在 Bash 中我运行:
alias myalias='echo foo
echo bar
echo baz'
myalias
返回:
foo
bar
baz
但:
ssh localhost "shopt -s expand_aliases &>/dev/null;
alias myalias='echo foo
echo bar
echo baz'
myalias"
返回:
foo
为什么?
答案1
我认为你发现了 Bash 的一个错误。此错误特定于选项-c
。
远程运行与您的多行别名问题无关。您可以在本地 bash 中尝试一下。但不是在 bash 脚本或交互式 bash 中,请尝试使用-c
选项,如下所示
bash -c "shopt -s expand_aliases &>/dev/null;
alias myalias='echo foo
echo bar
echo baz'
myalias"
与您的问题相同的输出。仅foo
打印。
为了获得正确的(预期的)输出,您必须至少在 后面添加一行myalias
,如 @cuonglm 建议的那样。
bash -c "shopt -s expand_aliases &>/dev/null;
alias myalias='echo foo
echo bar
echo baz'
myalias
:"
为什么会这样呢?为什么help后面多了一行myalias
?
我只想说这没有道理。 Bash 中没有任何文档解释或提及这种情况,一点也没有。它不应该以这种方式运行。这是一个错误。阅读完代码后,您就会确定这一点。
返回到第一个有问题的命令。这次什么都不用改,重新编译bash即可“ONESHOT”未定义,那么您将得到正确的(预期的)输出。是的,你没听错,由于不同的编译时配置,该命令有两种不同的行为。
无论是否定义ONESHOT
,都会导致 Bash 代码中出现两条完全不同的路由-c "command"
。如果取消定义 ONESHOT,-c "command"
将运行正常的代码路由,这是几乎所有 bash 执行的代码路由,例如交互式命令和 bash 脚本。但如果定义了ONESHOT,-c "command"
则会运行另一条专门为其设计的特定路线,以通过避免分叉来提高其性能。
对于这种情况,正常且最常用的方式可以给出正确的输出,而特定方式则不能。我认为不一致的行为并不是 Bash 作者想要的。至于哪种行为是正确的,我倾向于认为正常的方式是正确的。
有关此错误的一些详细信息
以下代码与该错误相关。它来自文件builtins/evalstring.c中的函数parse_and_execute()
while (*(bash_input.location.string))
{
...
}
该while
循环将按行运行,在一个循环中处理一行。在命令中的最后一行read 之后myalias
(见上文),条件while
将变为 false。myalias
扩展为三行回显,但本次循环只处理了一条回显;另外两个回声将在下一个循环中处理,但是......没有另一个循环。
myalias
如果在read 之后再添加一行,myalias
则 in 中的条件while
将保持为 true,因此另外两个 echo 将有机会在下一个循环中运行。之后的最后一行将在处理myalias
所有扩展的回声后进行处理。myalias
更新
忘了说这个问题涉及到的Bash版本是
GNU bash, version 4.4.12(1)-release (x86_64-pc-linux-gnu)
答案2
解决方法(受@cuonglm启发):
ssh localhost "shopt -s expand_aliases &>/dev/null;
alias myalias='ls
echo foo
echo bar
echo baz'
myalias &&
true"
这将保留退出代码。然而true
,必须走上新的路线。
它仍然没有解释为什么。但它看起来越来越像一个错误。