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值為'!'

相關內容