%20in%20Bash.png)
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?
! ls
funktioniert, ist jedoch dasselbe wie ls
.
! time ls
funktioniert 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 | cmd2
der !
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 if
definiert ist, dass er eine Liste zwischen den Schlüsselwörtern if
und 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önnencmd1 | ! 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“cmd2
bewirkt überhaupt nichts; das!
„Before“cmd4
beeinflusst 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 ! cmd
kann durch ersetzt werdenuntil 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
Bang
ist .%token
'!'