bash 中管道 (|) 和逻辑与 (&&) 的优先级

Rob*_*nde 27 bash

具有运算符优先级的经典场景,您有如下一行:

(cd ~/screenshots/ && ls screenshot* | head -n 5)
Run Code Online (Sandbox Code Playgroud)

你不知道它是否被解析((A && B) | C)(A && B | C)......

这里找到的几乎官方文档没有在列表中列出管道,所以我不能简单地检查表格。

此外,在 bash 中,(不仅用于更改操作顺序,还创建了一个 subshel​​l,所以我不是 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,它试图成为最可靠的。


Ser*_*nyy 9

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 5cd ~/screenshots/会首先执行,ls screenshot* | head -n 5如果前面的命令成功,但head -n 5可能是第一个进程产生的,而不是ls因为他们是在一个管道。


JoL*_*JoL 5

这是它在 中指定的地方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)

所以,&&分离管道。