Verwenden Sie $? in einer if-Anweisung

12
function foo {
   (cd $FOOBAR;
   <some command>
   if [$? -ne 0]
   then
      echo "Nope!"
   else
      echo "OK!"
   fi
   )
}

Ich versuche, eine Funktion wie die oben beschriebene zu schreiben und in meine .bashrc-Datei einzufügen. Nachdem ich die Datei bezogen und ausgeführt habe, erhalte ich:

Gesamtzeit: 51 Sekunden
-Bash: [1: Befehl nicht gefunden
OK!

Kann mir jemand helfen zu verstehen, was ich falsch gemacht habe?

Amir Afghani
quelle
4
Das Testen, ob $?mit einer ifAnweisung gleich 0 ist, ist sinnlos, iferwartet einen Befehl und wenn der Befehl zurückgegeben wird 0, wird der Code im Block ausgeführt. Also if true; then echo hello; fiwird Hallo hallo, da der Befehl truezurückgegeben wurde 0.
llua
1
@llua Es ist nicht sinnlos. $?Enthält den Status der letzten Pipeline , die nicht der Befehl test( [) in der ifAnweisung ist. Das Beispiel testet, ob some commandes erfolgreich war. Sie können dasselbe mit &&und tun ||, aber es können im Vergleich zu lange, unlesbare Zeilen entstehen if [ $? -eq 0 ]. Das gleiche Argument gilt fürif some command
Bonsaiviking
@bonsaiviking Ich bin mir sehr wohl bewusst, worauf es ankommt. Ich $?weise darauf hin, dass es keinen Sinn macht, testes zu verwenden. da if some commandmacht das gleiche mit einer Aussage im Vergleich zu zwei getrennten Aussagen. Wenn der Befehl bereits lang ist, würde das Hinzufügen von drei weiteren Zeichen das "unlesbare" nicht schrecklich "unlesbarer" machen.
llua

Antworten:

33

Fügen Sie ein Leerzeichen nach dem [und ein weiteres vor ]:

function foo {
   (cd $FOOBAR;
   <some command>
   if [ $? -ne 0 ]
   then
      echo "Nope!"
   else
      echo "OK!"
   fi
   )
}

[ein Shell - builtin ist, ist es ein Befehl wie echo, read, expr... es einen Raum , nachdem er braucht, und erfordert eine passende ].

Schreiben [ $? -ne 0 ]ist tatsächlich aufgerufen wird [und es vier Parameter geben: $?, -ne, 0, und ].

Hinweis: Die Tatsache, dass Sie eine Fehlermeldung erhalten, [1: command not foundbedeutet, $?dass der Wert von angegeben wurde 1.

Aularon
quelle
1
Ich habe gerade überprüft, ob Ihre Antwort auf meiner Linux-VM korrekt ist.
Samiam
[ist ein Link zu testals auch
Ricky Beam -
2
@ RickyBeam ist in den meisten Shells [eine eingebaute Shell und /usr/bin/[wird nur selten verwendet.
Patrick
20

Oder Sie könnten $?ganz überspringen . Wenn Ihr Befehl lautet cmd, sollte Folgendes funktionieren:

function foo {
   (cd $FOOBAR;
   if cmd
   then
      echo "OK!"
   else
      echo "Nope!"
   fi
   )
}
unxnut
quelle
6

Es wird empfohlen, den Rückgabewert einer Variablen zuzuweisen, bevor Sie ihn verwenden

retval="$?"
if [ $retval -ne 0 ]

Sie können den Rückgabewert wiederverwenden. zB in einer if ... elif ... else ... Anweisung.

Abdul
quelle
-1: Sie haben das Leerzeichen danach vergessen. [(Ohne das Leerzeichen wird es zu einem Syntaxfehler in bash) Wird rückgängig gemacht, wenn Sie Ihren Fehler bearbeiten und beheben.
Samiam
Ja, du hast recht. Ich habe es repariert.
Abdul
@samiam, dass der letzte Kommentar an Sie gerichtet war
terdon
4
:) Könnten Sie Ihre Antwort etwas erweitern. Warum ist es eine gute Praxis? Welche Fehler könnten vermieden werden?
Terdon
1
@terdon Es ist eine schlechte Praxis, $?direkt zu verwenden, da dies unterbrochen wird, wenn Sie beim späteren Bearbeiten des Skripts jemals eine Zeile zwischen dem Befehl und der $?Prüfung einfügen.
Samiam
3

Der einzige Grund, warum Sie $?als Argumente für einen [Befehl verwenden möchten (unabhängig davon, ob dieser [Befehl im Bedingungsteil einer ifAnweisung ausgeführt wird oder nicht), besteht darin, dass Sie einen bestimmten Rückgabestatus unterscheiden möchten, z.

until
  cmd
  [ "$?" -gt 1 ]
do
  something
done

Die Syntax für alle diejenigen if, while, until... Aussagen

if cmd-list1
then cmd-list2
else cmd-list3
fi

Welches läuft, cmd-list2wenn cmd-list1es erfolgreich ist oder cmd-list3nicht.

Der [ "$?" -eq 0 ]Befehl ist ein No-Op. Es wird $?auf 0 gesetzt, wenn $?0 ist, und $?auf ungleich Null, wenn es nicht Null war.

Wenn Sie etwas ausführen möchten, wenn es cmdfehlgeschlagen ist, ist es:

if ! cmd
then ...
fi

Im Allgemeinen müssen Sie nicht basteln, $?geschweige denn wissen, welcher Wert bedeutet trueoder false. Die einzigen Fälle sind, wie oben erwähnt, wenn Sie einen bestimmten Wert unterscheiden müssen oder wenn Sie ihn für später speichern müssen (um ihn beispielsweise als Rückgabewert einer Funktion zurückzugeben), wie:

f() {
  cmd; ret=$?
  some cleanup
  return "$ret"
}

Denken Sie auch daran, dass der Operator split + glob eine Variable ohne Anführungszeichen bleibt. Es ist nicht sinnvoll, diesen Operator hier aufzurufen, daher sollte es sein:

[ "$?" -ne 0 ]

nicht [ $? -ne 0 ], geschweige denn [$? -ne 0 ](was den [Befehl nur aufrufen würde, wenn $IFSes das erste Zeichen von enthalten würde $?).

Beachten Sie auch, dass die Bourne-Methode zum Definieren einer Funktion darin besteht, function-name()vor einem Befehl zu bleiben . Das ist der Fall in jedem Bourne wie Shell außer bashund yash(und neueren Versionen von posh) , die nur einer Verbindung Befehl (Verbindung Befehle zu sein erlauben {...}oder (...)oder Dinge wie for...done, if...fi...

function foo { ... }ist die kshSyntax der Funktionsdefinition. Es gibt keinen Grund, warum Sie es hier verwenden möchten.

Ihr Code kann portabel (POSIXly) geschrieben werden:

foo() (
  cd -P -- "$FOOBAR" || return # what if the cd failed!
  if
    <some command>
  then
    echo 'OK!'
  else
    echo 'Nope!'
  fi
)

Beachten Sie auch, dass cdwithout -Peine ganz besondere Bedeutung hat (behandelt Pfade, die ..Komponenten enthalten, die sich von anderen Befehlen unterscheiden). Daher ist es besser, sie in Skripte aufzunehmen, um Verwirrungen zu vermeiden.

(Diese Funktion wird zurückgegeben, falsewenn sie cdfehlschlägt, aber nicht, wenn sie <some command>fehlschlägt.)

Stéphane Chazelas
quelle
1

Ich glaube, der folgende Befehl wird alles, was Sie wollen, in einer Zeile tun.

(( verify = $?!=0?'Nope!':'OK!' ))
Jeight
quelle