具有运算符优先级的经典场景,您有如下一行:
(cd ~/screenshots/ && ls screenshot* | head -n 5)
Run Code Online (Sandbox Code Playgroud)
你不知道它是否被解析((A && B) | C)
或(A && B | C)
......
在这里找到的几乎官方文档没有在列表中列出管道,所以我不能简单地检查表格。
此外,在 bash 中,(
不仅用于更改操作顺序,还创建了一个 subshell,所以我不是 100% 确定此行与上一行等效:
((cd ~/screenshots/ && ls screenshot*) | head -n 5)
Run Code Online (Sandbox Code Playgroud)
更一般地说,如何知道bash 行的AST?在 python 中,我有一个函数可以为我提供树,以便我可以轻松地仔细检查操作顺序。
Mic*_*mer 31
cd ~/screenshots/ && ls screenshot* | head -n 5
Run Code Online (Sandbox Code Playgroud)
这相当于
cd ~/screenshots && { ls screenshot* | head -n 5 ; }
Run Code Online (Sandbox Code Playgroud)
(大括号将命令组合在一起,没有子外壳)。因此 的优先级|
高于(绑定更紧密) 比&&
和||
。那是,
A && B | C
Run Code Online (Sandbox Code Playgroud)
和
A || B | C
Run Code Online (Sandbox Code Playgroud)
总是意味着只将 B 的输出提供给 C。如有必要,您可以使用(...)
或{ ... ; }
将命令作为单个实体连接在一起以消除歧义:
{ A && B ; } | C
A && { B | C ; } # This is the default, but you might sometimes want to be explicit
Run Code Online (Sandbox Code Playgroud)
您可以使用一些不同的命令对此进行测试。如果你跑
echo hello && echo world | tr a-z A-Z
Run Code Online (Sandbox Code Playgroud)
然后你会得到
hello
WORLD
Run Code Online (Sandbox Code Playgroud)
back:tr a-z A-Z
大写它的 input,你可以看到只有echo world
通过管道进入它,同时echo hello
通过它自己。
这是在shell语法中定义的,虽然不是很清楚:and_or
产生式(for &&
/ ||
)被定义为pipeline
在其主体中具有aa ,而pipeline
只包含command
,不包含and_or
- 只有complete_command
产生式可以到达and_or
,并且它只存在于顶层和结构体内部,如函数和循环。
您可以手动应用该语法来获取命令的解析树,但 Bash 本身不提供任何内容。我不知道有任何 shell 超出了用于它们自己的解析的功能。
shell 语法有很多只半正式定义的特殊情况,要做到正确可能是一项艰巨的任务。甚至 Bash 本身有时也会出错,因此实用性和理想可能会有所不同。
有尝试匹配语法并生成树的外部解析器,其中我将广泛推荐Morbig,它试图成为最可靠的。
TL;DR:列出分隔符,例如;
. &
, &&
, 并||
决定解析顺序。
在bash的手册告诉我们:
AND 和 OR 列表是由 && 和 || 分隔的一个或多个管道的序列 分别控制运算符。
或者Bash Hacker 的 wiki 是如何简洁地表述的
<PIPELINE1> && <PIPELINE2>
Run Code Online (Sandbox Code Playgroud)
因此,cd ~/screenshots/ && ls screenshot* | head -n 5
有一个管道ls screenshot* | head -n 5
和一个简单的命令cd ~/screenshots/
。注意按照说明书
管道中的每个命令都作为单独的进程(即,在子外壳中)执行。
另一方面,(cd ~/screenshots/ && ls screenshot*) | head -n 5
是不同的 - 你有一个管道:左边有子外壳,右边有head -n 5
. 在这种情况下,使用 OP 的符号将是(A && B) | C
让我们再举一个例子:
$ echo foo | false && echo 123 | tr 2 5
$
Run Code Online (Sandbox Code Playgroud)
在这里,我们有一个列表<pipeline1> && <pipeline2>
。由于我们知道管道的退出状态与上一个命令的退出状态相同,并false
返回否定状态即失败,&&
因此不会执行右侧。
$ echo foo | true && echo 123 | tr 2 5
153
Run Code Online (Sandbox Code Playgroud)
这里左边的管道有成功退出状态,所以右边的管道被执行,我们看到了它的输出。
请注意,shell 语法并不意味着实际的执行顺序。引用吉尔斯的回答之一:
管道命令并发运行。当你运行 ps | grep ...,这取决于 ps 还是 grep 首先启动,无论如何它们会继续并发执行。
来自 bash 手册:
AND 和 OR 列表以左结合性执行。
在此基础上在cd ~/screenshots/ && ls screenshot* | head -n 5
该cd ~/screenshots/
会首先执行,ls screenshot* | head -n 5
如果前面的命令成功,但head -n 5
可能是第一个进程产生的,而不是ls
因为他们是在一个管道。
这是它在 中指定的地方bash(1)
:
SHELL GRAMMAR
[...]
Pipelines
A pipeline is a sequence of one or more commands separated by one of
the control operators | or |&.
[...]
Lists
A list is a sequence of one or more pipelines separated by one of the
operators ;, &, &&, or ||, and optionally terminated by one of ;, &, or
<newline>.
Run Code Online (Sandbox Code Playgroud)
所以,&&
分离管道。