我正在阅读 bash 源代码,并且BNF语法对于 bash 来说是:
<pipeline_command> ::= <pipeline>
| '!' <pipeline>
| <timespec> <pipeline>
| <timespec> '!' <pipeline>
| '!' <timespec> <pipeline>
<pipeline> ::=
<pipeline> '|' <newline_list> <pipeline>
| <command>
这是否意味着!
命令也是一种管道。
! ls
有效,但是它与 相同ls
。
! time ls
也有效。
这与|
管道完全不同。
如何!
在bash中使用?是管子吗?
答案1
来自 bash 手册:“如果保留字 ! 在管道之前,则该管道的退出状态是退出状态的逻辑非”
你误读了语法。语法说的是你可以放一个 !前面有一个管道,不是替换|与!
答案2
感叹号只是逻辑上反转命令/管道的返回代码(参见例如Bash 的手册):
if true ; then echo 'this prints' ; fi
if ! false ; then echo 'this also prints' ; fi
if ! true ; then echo 'this does not print' ; fi
管道的返回代码(通常)只是最后一个命令的返回代码,因此爆炸会反转:
if ! true | false ; then echo 'again, this also prints' ; fi
碰巧的是,我在 Bash 源代码发行版中看不到该 BNF 文件,并且引用的语法并不完全准确,因为自 Bash 4.2(2011 年发布)以来,Bash 确实接受多个感叹号:
if ! ! true ; then echo 'this prints too' ; fi
但这不是标准的,例如 zsh 和 Dash 就在这方面表现不佳。
答案3
将管道定义为一或多个命令意味着单个命令也是管道,尽管实际上并不涉及管道。好处是,!
作为否定运算符,不必为命令和管道单独定义;它只需定义为应用于管道。
在 中! cmd1 | cmd2
,!
取消整个管道的退出状态,而不仅仅是单个命令cmd1
。默认情况下,管道的退出状态是最右侧命令的退出状态。
同样,一个列表是又一条由;
、&
、&&
、 或相连的管道||
。因此,单个管道也是一个列表,单个命令也是一个列表。然后,当像这样的命令if
定义为采用if
和then
关键字之间的列表时,这会自动包括单个命令和单个管道作为命令定义的一部分。
由两个管道组成的列表(其中一个仅包含一个命令):
if IFS= read -r response && echo "$response" | grep foo; then
由单个管道组成的列表:
if echo "$foo" | grep foo; then
由单个管道组成的列表(其本身仅包含单个命令):
if true; then
答案4
在其他答案的基础上补充几点:
正如(间接)指出的切普纳的回答,
!
运算符被定义为语法元素的可选前缀<pipeline_command>
,而不是<command>
。这会带来你不能说的后果cmd1 | ! cmd2
或者
! cmd1 | ! cmd2
您只能否定“整个管道”的退出状态。正如切普纳指出的那样,“管道”可以是单个命令,因此您可以执行以下操作
! cmd1 && ! cmd2; ! cmd3 || ! cmd4
但这很愚蠢。 before
!
没有cmd2
做任何事; before!
只影响 命令末尾cmd4
的值,其他两个可以通过交换 AND 和 OR 来消除:$?
cmd1 || cmd2; cmd3 && cmd4
同样,
while ! cmd
可以替换为until cmd
.A
<pipeline>
前面有 a!
就变成了 a——<pipeline_command>
不同的句法元素。因此,说! ! cmd1
与算术扩展不同,算术扩展中像 和 之类的东西都是有效的。
$((! ! value))
$((! ! ! value))
请注意POSIX定义相同的语法,但使用不同的元素名称。问题中的 BNF 在 POSIX 中显示为
pipeline : pipe_sequence | Bang pipe_sequence pipe_sequence : command | pipe_sequence '|' linebreak command
其中
Bang
a 的%token
值为'!'
。