Bang (!) 在 bash 中的用法

Bang (!) 在 bash 中的用法

我正在阅读 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定义为采用ifthen关键字之间的列表时,这会自动包括单个命令和单个管道作为命令定义的一部分。

  • 由两个管道组成的列表(其中一个仅包含一个命令):

    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
    

    其中Banga 的%token值为'!'

相关内容