Warum bedeutet " wenn $(true) ; then ... fi` gelingen?
Inspiriert von diese Frage:
Was sollte eine if-Anweisung machen, wenn die Bedingung ist ein Kommando-substitution an dem der Befehl erzeugt keine Ausgabe?
HINWEIS: Das Beispiel ist if $(true); then ...
, nicht if true ; then ...
Beispiel:
if $(true) ; then echo yes ; else echo no ; fi
Ich würde denken, dass $(true)
sollte ersetzt werden durch die Ausgabe der true
Befehl, das ist nichts. Es soll dann gleichwertig sein, entweder diese:
if "" ; then echo yes ; else echo no ; fi
dem Drucke no
weil es keinen Befehl, dessen name ist ein leerer string, oder so:
if ; then echo yes ; else echo no ; fi
ist ein syntax-Fehler.
Aber das experiment zeigt, dass, wenn der Befehl erzeugt keine Ausgabe, die if
- Anweisung behandelt es als wahr oder falsch in Abhängigkeit vom status des Befehls, sondern als dessen Ausgang.
Hier ist ein Skript, das zeigt das Verhalten:
#!/bin/bash
echo -n 'true: ' ; if true ; then echo yes ; else echo no ; fi
echo -n 'false: ' ; if false ; then echo yes ; else echo no ; fi
echo -n '$(echo true): ' ; if $(echo true) ; then echo yes ; else echo no ; fi
echo -n '$(echo false): ' ; if $(echo false) ; then echo yes ; else echo no ; fi
echo -n '$(true): ' ; if $(true) ; then echo yes ; else echo no ; fi
echo -n '$(false): ' ; if $(false) ; then echo yes ; else echo no ; fi
echo -n '"": ' ; if "" ; then echo yes ; else echo no ; fi
echo -n '(nothing): ' ; if ; then echo yes ; else echo no ; fi
und hier ist die Ausgabe die ich bekomme (Ubuntu 11.04, bash 4.2.8):
true: yes
false: no
$(echo true): yes
$(echo false): no
$(true): yes
$(false): no
"": ./foo.bash: line 9: : command not found
no
./foo.bash: line 10: syntax error near unexpected token `;'
./foo.bash: line 10: `echo -n '(nothing): ' ; if ; then echo yes ; else echo no ; fi'
Den ersten vier Zeilen Verhalten sich, als ich erwarten würde; die $(true)
und $(false)
Linien sind überraschend.
Weiteren experiment (hier nicht dargestellt) zeigt an, dass der Befehl zwischen $(
und )
output erzeugt, dessen exit-status keinen Einfluss auf das Verhalten der if
.
Sehe ich ein ähnliches Verhalten (aber verschiedene Fehlermeldungen in einigen Fällen) mit bash
, ksh
, zsh
, ash
, und dash
.
Sehe ich nichts in der bash-Dokumentation, oder in der POSIX-Shell Command Language", Spezifikation, dies zu erklären.
(Oder vielleicht bin ich etwas fehlt offensichtlich.)
EDIT : Im Licht der akzeptierten Antwort, hier ist ein weiteres Beispiel für das Verhalten:
command='' ; if $command ; then echo yes ; else echo no ; fi
oder, was dasselbe ist:
command= ; if $command ; then echo yes ; else echo no ; fi
- Das ist sehr interessant. Wie auch immer, ich denke es ist ein Fehler in deiner Frage. Sie sagte, dass
if "" ; then echo yes ; else echo no ; fi
gibt keine, weil 'es ist kein Befehl, dessen name ist ein leerer string", aber durch die gleiche Logik sollte es zu einem Fehler, der so etwas wie "<leere Zeichenfolge>: Befehl nicht gefunden". Es gibt einige andere Unterschied zwischenif "" ; then echo yes ; else echo no ; fi
undif "nonexistant" ; then echo yes ; else echo no ; fi
- Ich denke, es gibt einen Fehler in Ihrem Kommentar.
if "nonexistent" ; then …
die Fehlermeldung erzeugtbash: nonexistent: command not found
und dann sagtno
. Durch Vergleichif "" ; then …
die Fehlermeldung erzeugtbash: : command not found
und dann sagtno
. Das ist genau das gleiche Verhalten, außernonexistent
wurde gelöscht (d.h., ersetzt durch nichts). Es ist die gleiche Nachricht, die Sie erhalten, wenn Sie geben Sie nur""
(gefolgt von <Enter>). Können Sie uns ein Beispiel geben von einem Fall, wo die bash sagt so etwas wie<empty string>
anstatt nur zu schreiben, eine leere Zeichenfolge? ... (Fortsetzung) - (Fortsetzung) ... Können Sie identifizieren die tatsächliche Differenz zwischen
if "" ; then …
undif "nonexistent" ; then …
? - Ich bin damit einverstanden, dass dies eine interessante Frage. Mit all den erläuternden Beispielen vorgestellt auf dieser Seite, ich bin überrascht, dass niemand hat angeboten
if "$(true)" ; then …
undcommand='' ; if "$command" ; then …
(mit Anführungszeichen), die das gleiche tun wieif "" ; then …
.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Siehe Abschnitt 2.9.1 des language spec. Der Letzte Satz des ersten Abschnitts lautet:
Den
$(true)
erweitert, um die leere Zeichenfolge. Die shell analysiert die leeren string und stellt fest, dass kein weiterer Befehl gegeben wird und folgt der oben genannten Regel.if $(true) $(false)
bewerten diefalse
letzten?" Die Antwort liegt in dem zweiten Satz von Abschnitt 2.9.1: "Wenn ein gegebener einfacher Befehl wird benötigt, um ausgeführt werden ..., die folgenden Erweiterungen Aufgaben, und Umleitungen sind alle durchgeführt werden aus dem Anfang des Befehls text am Ende" (Hervorhebung Hinzugefügt).Gibt es möglicherweise zwei exit-codes zu berücksichtigen. Zuerst, hier ist ein weiteres zwei Experimente, die sollte helfen:
Den inneren Befehl Ausfahrten mit Versagen, weil der
false
. Aber das ist irrelevant, da die Ausgabe des Befehls nicht leer ist und daher der Ausgang ("wahr") ausgeführt wird, statt und dessen exit-code eine höhere Priorität.Wieder, die Befehl Linie innerhalb der
$( )
erfolgreich ist, aber die Ausgabe ist nicht, da die Ausgabe ("falsch") Vorrang.Den exit-status des Kommandos innerhalb des
$( )
relevant ist , wenn, und nur wenn der Ausgang leer ist oder nur Leerzeichen. Wenn die Ausgabe leer ist, gibt es keine Liste von Befehlen und folglich scheint es, dass die Schale fallen zurück auf den exit-status des innere Befehl.$(true)
und$(false)
sub würde für null-Befehle, die nicht zählen für die Ausführung und damit der exit-code vontrue
- und/oderfalse
bleiben.$( )
, was war der Letzte Befehl ausgeführt?Was anscheinend passiert, ist, dass
bash
hält der exit-status von die letzten ausgeführten BefehlDies würde erklären, warum
$(true)
und$(false)
unterschiedliche Verhalten in einerif
test. Beide produzieren null-Befehle, die nicht zählt als die Ausführung, aber Sie haben unterschiedliche exit-codes.Sobald Sie das Kommando-substitution auf einen Befehl, ausgegeben hat,
$()
versuche ausgeführt, dass als Ausgabe ein Befehl und der exit-code von , dass Versuch ist jetzt die aktuellste für denif
testAusgang spielt keine Rolle. Was zählt, ist die exit-code:
Wenn Sie ersetzen Ihre
$(echo true)
und$(echo false)
mit etwas anderes, werden Sie wahrscheinlich sehen, was Los ist:Den
$(..)
führt den Befehl aus und dannif
führt die Ergebnisse (in diesem Fall nurtrue
oderfalse
oderdoes-not-exist
). Eine leere$()
startet eine subshell, die erfolgreich zum Abschluss ausgeführt und gibt einen exit-code von0
:Aaron hob einige interessante Punkte:
Scheint es, dass die Ausführung des Befehls erstellt durch die
$()
subshell, den ersten Befehl der exit-status ist es, was zählt.$( )
egal, wenn die Ausgabe nicht leer ist.if $(echo true; false) ; then echo yes ; else echo no ; fi
gibtyes
. Also diefalse
ignoriert wird, weil der ausgegeben wird - Befehl ("true") ausgeführt wird, statt.no
:if $(false;echo 'true ; false') ; then echo yes ; else echo no ; fi
$()
subshell in Ihrem Beispiel isttrue
. Vergessen Sie nicht, dassif
führt die Ergebnisse der Ausgabe der$()
... (Wenn Sie vorschlagen, die bessere Formulierung, bitte nicht. Ich ging um auf Kreise über die spezifische Satz für fünf Minuten, bevor Sie sich auf die.)$()
startet eine subshell, die erfolgreich zum Abschluss ausgeführt und gibt einen exit-code von0
" -- Das scheint der Fall zu sein; die Frage warum? Wo ist dieses Verhalten dokumentiert?if $(echo 'true ; false'); then ...
undif true ; false; then ...
geben unterschiedliche Ergebnisse ist, dass die Kommando-substitution ist nur erfährt word-splitting und glob Erweiterung, nichts mehr. Daher$(echo 'true ; false')
erweitert, umtrue \; false
, d.h.true
mit zwei Argumenten aufgerufen:;
undfalse
und nicht zwei separate Befehleif $(echo 'ls -l false ; true') ; then …
, die Ausgängefalse: No such file
,;: No such file
, undtrue: No such file
(es sei denn, Sie haben Dateien mit diesen Namen), und dannno
. Dies zeigt, dass die;
wird interpretiert als ein argument und nicht ein kommandotrenner. Auf der anderen Seiteif $(echo 'eval false ; true') ; then …
Ausgängeyes
, weil dieeval
bewirkt, dass die shell interpretieren die;
als Trennzeichen, und ausführenfalse
und danntrue
. (Aber kommen Sie nicht in die Gewohnheit, miteval
; im Gegenteil, Sie sollten versuchen, es zu vermeiden.)