Verwendung von Bang (!) in Bash

Verwendung von Bang (!) in Bash

Ich lese den Bash-Quellcode und dieBNF-Grammatikfür Bash wäre:

<pipeline_command> ::= <pipeline>
                    |  '!' <pipeline>
                    |  <timespec> <pipeline>
                    |  <timespec> '!' <pipeline>
                    |  '!' <timespec> <pipeline>

<pipeline> ::=
          <pipeline> '|' <newline_list> <pipeline>
       |  <command>

Bedeutet dies, dass !der Befehl auch eine Art Pipe ist?

! lsfunktioniert, ist jedoch dasselbe wie ls.

! time lsfunktioniert auch.

Das ist etwas ganz anderes als |Pipe.

Wie wird es in Bash verwendet !? Ist es eine Pipe?

Antwort1

Aus dem Bash-Handbuch: „Wenn das reservierte Wort ! einer Pipeline vorangestellt ist, ist der Beendigungsstatus dieser Pipeline die logische Negierung des Beendigungsstatus.“

Sie lesen die Grammatik falsch. Die Grammatik besagt, dass Sie vor eine Pipeline ein ! setzen können und nicht | durch ein ! ersetzen dürfen.

Antwort2

Das Ausrufezeichen kehrt lediglich den Rückgabecode des Befehls/der Pipeline logisch um (siehe z. B.Bashs Handbuch):

if true ;    then echo 'this prints' ; fi
if ! false ; then echo 'this also prints' ; fi
if ! true ;  then echo 'this does not print' ; fi

Der Rückgabecode einer Pipeline ist (normalerweise) einfach der Rückgabecode des letzten Befehls, daher kehrt der Bang das um:

if ! true | false ; then echo 'again, this also prints' ; fi

Zufällig kann ich diese BNF-Datei in der Bash-Quellverteilung nicht sehen und die zitierte Grammatik ist nicht ganz korrekt, da Bash seit Bash 4.2 (veröffentlicht 2011) mehrere Bangs akzeptiert:

if ! ! true ; then echo 'this prints too' ; fi

Das ist jedoch kein Standard, und beispielsweise zsh und Dash scheitern daran.

Antwort3

Definieren einer Pipeline alseinsoder mehrere Befehle bedeutet, dass ein einzelner Befehl auch eine Pipeline ist, wenn auch eine, die eigentlich keine Pipe beinhaltet. Der Vorteil besteht darin, dass !ein Negationsoperator nicht separat für Befehle und Pipelines definiert werden muss; er muss nur als auf eine Pipeline anwendbar definiert werden.

In negiert ! cmd1 | cmd2der !den Beendigungsstatus der gesamten Pipeline, nicht nur des einzelnen Befehls cmd1. Der Beendigungsstatus einer Pipeline ist standardmäßig der Beendigungsstatus des Befehls ganz rechts.


EbensoListeist eine oder mehrere Pipelines, die durch ;, &, &&, oder verbunden sind ||. Daher ist eine einzelne Pipeline auch eine Liste und ein einzelner Befehl ist auch eine Liste. Wenn dann ein Befehl wie so ifdefiniert ist, dass er eine Liste zwischen den Schlüsselwörtern ifund annimmt then, schließt dies automatisch einzelne Befehle und einzelne Pipelines als Teil der Definition eines Befehls ein.

  • Eine Liste bestehend aus zwei Pipelines (von denen eine nur aus einem Befehl besteht):

    if IFS= read -r response && echo "$response" | grep foo; then
    
  • Eine Liste, die aus einer einzelnen Pipeline besteht:

    if echo "$foo" | grep foo; then
    
  • Eine Liste, die aus einer einzigen Pipeline besteht (die selbst nur einen einzigen Befehl enthält):

    if true; then
    

Antwort4

Zu den Aussagen der anderen Antworten möchte ich noch ein paar Punkte ergänzen:

  • Wie (indirekt) bemerkt vonChepners Antwortwird der !Operator als optionales Präfix für das <pipeline_command>syntaktische Element definiert, statt für <command>. Dies hat zur Folge, dass Sie nicht sagen können

      cmd1 | ! cmd2
    

    oder

    ! cmd1 | ! cmd2
    

    Sie können nur den Exit-Status einer „gesamten Pipeline“ negieren. Wie Chepner betonte, kann eine „Pipeline“ ein einzelner Befehl sein, sodass Sie Dinge tun können wie

    ! cmd1 && ! cmd2; ! cmd3 || ! cmd4
    

    aber das ist albern. Das „ !Before“ cmd2bewirkt überhaupt nichts; das !„Before“ cmd4beeinflusst nur den Wert $? am Ende des Befehls, und die anderen beiden können durch Austauschen von „AND“ und „OR“ eliminiert werden:

      cmd1  ||  cmd2;   cmd3  &&  cmd4
    

    Ebenso while ! cmdkann durch ersetzt werden until cmd.

  • Ein <pipeline>vorangestelltes a !wird zu einem a <pipeline_command>— einem anderen syntaktischen Element. Daher ist es nicht gültig zu sagen

    ! ! cmd1
    

    anders als bei der arithmetischen Erweiterung, wo Dinge wie und gültig sind.$((! ! value))$((! ! ! value))

  • Beachten Sie, dassPOSIXdefiniert dieselbe Grammatik, verwendet aber unterschiedliche Elementnamen. Die BNF in der Frage erscheint in POSIX als

    pipeline         :      pipe_sequence
                     | Bang pipe_sequence
    
    pipe_sequence    :                             command
                     | pipe_sequence '|' linebreak command
    

    wobei a mit dem Wert Bangist .%token'!'

verwandte Informationen