Ole Tange Asked: 2017-11-24 15:07:11 +0800 CST2017-11-24 15:07:11 +0800 CST 2017-11-24 15:07:11 +0800 CST Bash:为什么远程运行时忽略换行符后的别名? 772 在 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 为什么? bash alias 2 个回答 Voted Best Answer Bruce 2017-11-26T18:30:28+08:002017-11-26T18:30:28+08:00 我认为您发现了 Bash 的错误。此错误特定于 option -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 :" 为什么会这样?为什么myalias帮助后多一行? 我只想说这没有意义。Bash 中没有任何文档解释或提及这个案例,一点也没有。它不应该以这种方式运行。这是一个错误。阅读代码后,您将确定这一点。 回到第一个有问题的命令。这次不要改变任何东西,只需用 "ONESHOT" undefined重新编译 bash ,然后你就会得到正确的(预期的)输出。是的,你没听错,命令有两种不同的行为,只是因为不同的编译时配置。 无论是否定义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将保持为真,因此另外两个 echo 将有机会在下一个循环中运行。after 的最后一行myalias将在处理完所有展开的回声后myalias处理。 更新 我忘了说这个问题涉及的 Bash 版本,即 GNU bash, version 4.4.12(1)-release (x86_64-pc-linux-gnu) Ole Tange 2017-11-26T12:41:42+08:002017-11-26T12:41:42+08:00 解决方法(受@cuonglm 启发): ssh localhost "shopt -s expand_aliases &>/dev/null; alias myalias='ls echo foo echo bar echo baz' myalias && true" 这将保留退出代码。但是true,必须在新行上。 它仍然没有解释为什么。但它越来越像一个错误。
我认为您发现了 Bash 的错误。此错误特定于 option
-c
。远程运行与您关于多行别名的问题无关。您可以在本地 bash 中尝试。但不在 bash 脚本或交互式 bash 中,请使用
-c
选项尝试,像这样与您的问题相同的输出。仅
foo
打印。myalias
如@cuonglm 所建议的,要获得正确的(预期的)输出,您必须至少在 之后再添加一行。为什么会这样?为什么
myalias
帮助后多一行?我只想说这没有意义。Bash 中没有任何文档解释或提及这个案例,一点也没有。它不应该以这种方式运行。这是一个错误。阅读代码后,您将确定这一点。
回到第一个有问题的命令。这次不要改变任何东西,只需用 "ONESHOT" undefined重新编译 bash ,然后你就会得到正确的(预期的)输出。是的,你没听错,命令有两种不同的行为,只是因为不同的编译时配置。
无论是否定义
ONESHOT
都会导致 Bash 代码中的两条完全不同的路径-c "command"
。如果取消定义 ONESHOT,-c "command"
将运行正常的代码路径,这是几乎所有 bash 执行的代码路径,例如交互式命令和 bash 脚本。但是如果定义 ONESHOT,-c "command"
将运行另一条专门为其设计的特定路线,以通过避免分叉来提高其性能。对于这种情况,正常且最常用的方式可以提供正确的输出,而特殊方式则不能。我认为不一致的行为不是 Bash 作者想要的。至于哪种行为是对的,我倾向于认为正常的方式是对的。
关于这个错误的一些细节
以下代码与该错误有关。它来自文件 builtins/evalstring.c 中的函数 parse_and_execute()
该
while
循环将逐行运行,一个循环处理一行。在 read 之后myalias
,命令中的最后一行(见上文)中的条件while
将变为 false。myalias
扩展为三行回声,但在这个循环中只处理了一个回声;另外两个回声将在下一个循环中处理,但是......没有另一个循环。myalias
如果在 read之后再添加一行myalias
,则 in 中的条件while
将保持为真,因此另外两个 echo 将有机会在下一个循环中运行。after 的最后一行myalias
将在处理完所有展开的回声后myalias
处理。更新
我忘了说这个问题涉及的 Bash 版本,即
解决方法(受@cuonglm 启发):
这将保留退出代码。但是
true
,必须在新行上。它仍然没有解释为什么。但它越来越像一个错误。