Linux命令替换顺序

Linux命令替换顺序

我对命令替换感到困惑。我认为命令替换就像编程语言宏。在评估父命令之前,首先执行子 shell,并$(...)用其标准输出替换 。但这就是全部真相吗?

当我尝试执行以下命令时

echo {0..9} | xargs -n 2 $(echo 'echo | tac')

我希望看到以下输出

8 9
6 7
4 5
2 3
0 1

但相反得到这个

| tac 0 1
| tac 2 3
| tac 4 5
| tac 6 7
| tac 8 9

那么是什么原因导致| tac被捕获为 echo 的参数,而不是作为命令行的一部分呢?确切的机制和顺序是什么,如何解析、确定范围和评估命令行和命令替换?是否有可能以元编程方式在 Linux 中动态构建命令行?

编辑

我知道我可以使用echo {0..9}| xargs -n 2 | tac.这个问题是理论上的,我感兴趣为什么子 shell 示例没有产生相同的结果

答案1

您所看到的问题的根源是由于命令行的标记化发生方式所致。在这种特殊情况下,早在$(echo 'echo | tac')展开之前,shell 就已经知道您打算运行一个命令 ( echo {…}) 并通过管道 ( |) 将其输出传递给另一个命令 ( xargs -n 2 $(…))。

然后在下一阶段,填充$(…)和大括号扩展{…}是否会生成要运行的实际命令。在这个阶段,如果它产生了管道字符,那又怎么样,已经太晚了,而且显然错过了公共汽车。

现在,它将不被视为特殊元字符,而是将xargs作为任何普通(=>非元字符)字符包含在命令行中。

如果你想再试一次,那么你就需要这样做eval

eval "echo {0..9} | xargs -n 2 $(echo 'echo | tac')"

这将输出您期望的内容。

答案2

除了罗马所说,你的命令应该是这样的

echo {0..9}| xargs -n 2 | tac

这会产生

8 9
6 7 
4 5
2 3
0 1

使用毫无意义的 $(echo 'echo') 这会导致错误

附言。对于长答案,|将输出从一个命令重定向到下一个命令,因此echo {0..9}生成:

0 1 2 3 4 5 6 7 8 9

xargs -n 2接受该输入并产生

0 1
2 3
4 5 
6 7
8 9

最后tac获取该输入并生成相反的表达式:

8 9
6 7
4 5
2 3
0 1

我希望这更清楚一些。

编辑:

echo {0..9} | xargs -n 2 $(echo 'echo | tac')
+ echo 0 1 2 3 4 5 6 7 8 9
++ echo 'echo | tac'
+ xargs -n 2 echo '|' tac
| tac 0 1
| tac 2 3
| tac 4 5
| tac 6 7
| tac 8 9

这是您使用初始语法发送到 bash 的命令,很清楚发生了什么,最后您发送xargs -n 2 echo '|' tac

答案3

冒着多余的风险(但我没有看到其他人在哪里明确说过这一点),你的原因

echo {0..9} | xargs -n 2 $(echo 'echo | tac')

命令的作用是它相当于

echo {0..9} | xargs -n 2 'echo' '|' 'tac'

相比于

echo {0..9} | xargs -n 2 echo foo bar

发生这种情况是因为 是在解析$(echo …)命令行 ( ) 的整体结构之后进行评估的,因此 的输出只能向命令添加参数。要以您想要的方式做您想做的事,您需要说echo (something) | xargs (args)$(echo …)xargs

回声{0..9} |评估xargs -n 2 $(echo 'echo | tac')

请注意,这eval可能很危险。搜索此网站以获取有关此内容的警告,并注意它们。

相关内容