Bedeutung von $? (Dollar Fragezeichen) in Shell-Skripten

178

Was macht

echo $?

meine in der Shell-Programmierung?

Harshay Buradkar
quelle
Ausgangsstatus des letzten Befehls
Shiwangini

Antworten:

212

Dies ist der Exit-Status des zuletzt ausgeführten Befehls.

Beispielsweise gibt der Befehl trueimmer den Status von 0und falseimmer den Status von zurück 1:

true
echo $? # echoes 0
false
echo $? # echoes 1

Aus dem Handbuch: (zugänglich durch Aufrufen man bashIhrer Shell)

$?       Wird auf den Exit-Status der zuletzt ausgeführten Vordergrund-Pipeline erweitert.

Konventionell bedeutet ein Exit-Status 0Erfolg, und ein Rückgabestatus ungleich Null bedeutet Misserfolg. Weitere Informationen zu Exit-Status finden Sie auf Wikipedia .

Es gibt andere spezielle Variablen wie diese, wie Sie in diesem Online-Handbuch sehen können: https://www.gnu.org/s/bash/manual/bash.html#Special-Parameters

Arnaud Le Blanc
quelle
Beachten Sie $und ?sind zwei unterschiedliche Parameter und $?erscheinen nicht in der Bash (1) Manpage.
Josh Habdas
19

$?Gibt den Exit-Wert des zuletzt ausgeführten Befehls zurück. echo $?druckt diesen Wert auf der Konsole. Null bedeutet eine erfolgreiche Ausführung, während Werte ungleich Null verschiedenen Fehlerursachen zugeordnet werden.

Daher beim Scripting; Ich neige dazu, die folgende Syntax zu verwenden

if [ $? -eq 0 ]; then
 # do something
else
 # do something else
fi

Der Vergleich ist gleich 0oder nicht gleich durchzuführen 0.

** Update Basierend auf dem Kommentar: Idealerweise sollten Sie den obigen Codeblock nicht zum Vergleich verwenden. Weitere Informationen finden Sie in den Kommentaren und Erläuterungen zu @tripleee.

Saurabh Ariyan
quelle
15
Nein, das ist ein Antimuster. Alles, was aussieht, cmd; if [ $? -eq 0 ]; thensollte überarbeitet werden if cmd; then. Der eigentliche Zweck von if(und den anderen Flusssteuerungsanweisungen in der Shell) besteht darin, einen Befehl auszuführen und seinen Beendigungsstatus zu überprüfen.
Tripleee
if cmd;Möglicherweise sind einige Bedingungen nicht sehr gut lesbar, insbesondere wenn cmd auf ein anderes Skript verweist.
Saurabh Ariyan
1
Das ist jetzt noch falscher. [ 1 ]und [ 0 ]sind beide wahr; [ohne Operator prüft, ob das Argument eine nicht leere Zeichenfolge ist.
Tripleee
2
Ich bin im Begriff zu tun vendor/bin/drush status bootstrap | grep -q $(vendor/bin/drush php-eval 'if (function_exists("t")) echo t("Successful");') &> /dev/null;. Wenn ich das in eine einzige Zeile setzen if [ ... ]müsste, wäre es furchtbar unlesbar. Ich habe vor, die Ausgabe dieser Zeile in einer Variablen zu speichern, damit ich es if [ $drupal_installed -eq 0 ]später noch sagen kann .
Thirdender
1
@thirdender Die richtige Lösung besteht darin, den komplexen Test in einer Shell-Funktion zu kapseln.
Tripleee
12

echo $? - Gibt den EXIT STATUS des zuletzt ausgeführten Befehls an . Dieser EXIT-STATUS ist höchstwahrscheinlich eine Zahl, bei der NULL Erfolg bedeutet und jeder NON-ZERO-Wert Fehler anzeigt

? - Dies ist ein spezieller Parameter / eine spezielle Variable in Bash.

$? - Es gibt den in der Variablen "?" Gespeicherten Wert an.

Einige ähnliche spezielle Parameter in BASH sind 1,2, *, # (normalerweise im Echo-Befehl als $ 1, $ 2, $ *, $ # usw. zu sehen).

Arun Karthik
quelle
8

Es hat den letzten Statuscode (Exit-Wert) eines Befehls.

Ned Batchelder
quelle
5

Beispiel für einen minimalen POSIX C-Exit-Status

Um dies zu verstehen $?, müssen Sie zuerst das Konzept des Prozess-Exit-Status verstehen, das von POSIX definiert wird . Unter Linux:

  • Wenn ein Prozess den exitSystemaufruf aufruft , speichert der Kernel den an den Systemaufruf übergebenen Wert (anint ) übergebenen auch nachdem der Prozess beendet ist.

    Der Exit - Systemaufruf wird durch die genannte exit()ANSI - C - Funktion, und indirekt , wenn Sie tun , returnaus main.

  • Der Prozess, der den Prozess "Exiting Child" (Bash) aufgerufen hat, häufig mit fork+ exec, kann den Exit-Status des Kindes mit dem waitSystemaufruf abrufen

Betrachten Sie den Bash-Code:

$ false
$ echo $?
1

Das C "Äquivalent" ist:

false.c

#include <stdlib.h> /* exit */

int main(void) {
    exit(1);
}

bash.c

#include <unistd.h> /* execl */
#include <stdlib.h> /* fork */
#include <sys/wait.h> /* wait, WEXITSTATUS */
#include <stdio.h> /* printf */

int main(void) {
    if (fork() == 0) {
        /* Call false. */
        execl("./false", "./false", (char *)NULL);
    }
    int status;
    /* Wait for a child to finish. */
    wait(&status);
    /* Status encodes multiple fields,
     * we need WEXITSTATUS to get the exit status:
     * http://stackoverflow.com/questions/3659616/returning-exit-code-from-child
     **/
    printf("$? = %d\n", WEXITSTATUS(status));
}

Kompilieren und ausführen:

g++ -ggdb3 -O0 -std=c++11 -Wall -Wextra -pedantic -o bash bash.c
g++ -ggdb3 -O0 -std=c++11 -Wall -Wextra -pedantic -o false false.c
./bash

Ausgabe:

$? = 1

Wenn Sie in Bash die Eingabetaste drücken, geschieht eine Gabelung + Exec + Wait wie oben, und Bash wird dann gesetzt $? auf den Exit-Status des Forked-Prozesses.

Hinweis: Für integrierte Befehle wie echomuss kein Prozess erzeugt werden, und Bash wird nur festgelegt$? auf 0 gesetzt, um einen externen Prozess zu simulieren.

Standards und Dokumentation

POSIX 7 2.5.2 "Spezielle Parameter" http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_05_02 :

? Wird auf den dezimalen Exit-Status der letzten Pipeline erweitert (siehe Pipelines).

man bash "Spezielle Parameter":

Die Shell behandelt mehrere Parameter speziell. Auf diese Parameter darf nur verwiesen werden. eine Zuordnung zu ihnen ist nicht zulässig. [...]

? Wird auf den Exit-Status der zuletzt ausgeführten Vordergrund-Pipeline erweitert.

ANSI C und POSIX empfehlen dann Folgendes:

  • 0 bedeutet, dass das Programm erfolgreich war

  • andere Werte: Das Programm ist irgendwie fehlgeschlagen.

    Der genaue Wert kann die Art des Fehlers angeben.

    ANSI C definiert nicht die Bedeutung von Vaues, und POSIX gibt Werte größer als 125 an: Was bedeutet "POSIX"?

Bash verwendet den Exit-Status für if

In Bash verwenden wir den Exit-Status häufig $?implizit, um ifAnweisungen wie folgt zu steuern :

if true; then
  :
fi

Wo trueist ein Programm, das nur 0 zurückgibt.

Das Obige entspricht:

true
result=$?
if [ $result = 0 ]; then
  :
fi

Und in:

if [ 1 = 1 ]; then
  :
fi

[ist nur ein Programm mit einem seltsamen Namen (und einem integrierten Bash, der sich so verhält) und 1 = 1 ]seinen Argumenten, siehe auch: Unterschied zwischen einfachen und doppelten eckigen Klammern in Bash

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
quelle
3

Siehe das Bash-Handbuch unter 3.4.2 Spezielle Parameter :

? - Wird auf den Exit-Status der zuletzt ausgeführten Vordergrund-Pipeline erweitert.

Es ist etwas schwer zu finden, da es nicht als aufgeführt ist $?(der Variablenname ist "nur" ?). Siehe natürlich auch den Exit-Status- Abschnitt ;-)

Viel Spaß beim Codieren.

Shubham
quelle
2

Gibt das Ergebnis des zuletzt ausgeführten Unix-Befehls aus

0 implies true
1 implies false
Sojourner
quelle