Significato di $? (punto interrogativo in dollari) negli script shell


Risposte:


212

Questo è lo stato di uscita dell'ultimo comando eseguito.

Ad esempio il comando truerestituisce sempre uno stato di 0e falserestituisce sempre uno stato di 1:

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

Dal manuale: (accessibile chiamando man bashla shell)

$?       Si espande allo stato di uscita della pipeline in primo piano eseguita più di recente.

Per convenzione, uno stato di uscita 0significa successo e uno stato di ritorno diverso da zero significa fallimento. Ulteriori informazioni sugli stati di uscita su Wikipedia .

Esistono altre variabili speciali come questa, come puoi vedere in questo manuale online: https://www.gnu.org/s/bash/manual/bash.html#Special-Parameters


Nota $e ?sono due parametri distinti e $?non appare nella manpage bash (1).
Josh Habdas,

19

$?restituisce il valore di uscita dell'ultimo comando eseguito. echo $?stampa quel valore sulla console. zero implica un'esecuzione corretta mentre i valori diversi da zero sono associati a vari motivi di errore.

Quindi quando lo scripting; Tendo a usare la sintassi seguente

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

Il confronto deve essere fatto su uguale 0o meno uguale 0.

** Aggiornamento basato sul commento: idealmente, non si dovrebbe usare il blocco di codice sopra per il confronto, fare riferimento ai commenti e alla spiegazione di @tripleee.


15
No, questo è un antipasto. Tutto ciò che sembra cmd; if [ $? -eq 0 ]; thendovrebbe essere rifattorizzato if cmd; then. Lo scopo stesso di if(e delle altre istruzioni di controllo del flusso nella shell) è eseguire un comando ed esaminarne lo stato di uscita.
Tripleee

if cmd;potrebbe non essere molto leggibile è alcune condizioni soprattutto quando cmd si riferisce a un altro script.
Saurabh Ariyan,

1
Questo è ancora più sbagliato adesso. [ 1 ]e [ 0 ]sono entrambi veri; [senza un operatore controlla se l'argomento è una stringa non vuota.
tripleee,

2
Sto per fare vendor/bin/drush status bootstrap | grep -q $(vendor/bin/drush php-eval 'if (function_exists("t")) echo t("Successful");') &> /dev/null;. Se dovessi metterlo in una sola riga if [ ... ]sarebbe terribilmente illeggibile. Ho intenzione di archiviare l'output di quella riga in una variabile in modo da poterlo dire in if [ $drupal_installed -eq 0 ]seguito.
terzo

1
@thirdender La soluzione corretta è quella di incapsulare il test complesso in una funzione shell.
Tripleee

12

echo $? - Fornisce lo STATO DI USCITA del comando eseguito più di recente . Questo STATO DI USCITA sarebbe probabilmente un numero con ZERO che implica Successo e qualsiasi valore NON-ZERO che indica Fallimento

? - Questo è un parametro / variabile speciale in bash.

$? - Fornisce il valore memorizzato nella variabile "?".

Alcuni parametri speciali simili in BASH sono 1,2, *, # (normalmente visti nel comando echo come $ 1, $ 2, $ *, $ #, ecc.).



5

Esempio di stato di uscita C POSIX minimo

Per capire $?, devi prima capire il concetto di stato di uscita del processo che è definito da POSIX . In Linux:

  • quando un processo chiama la exitchiamata di sistema, il kernel memorizza il valore passato alla chiamata di sistema (an int) anche dopo la fine del processo.

    La chiamata di sistema di uscita è chiamato dal exit()funzione di ANSI C, e, indirettamente, quando si fa returnda main.

  • il processo che ha chiamato il processo figlio uscente (Bash), spesso con fork+ exec, può recuperare lo stato di uscita del figlio con la waitchiamata di sistema

Considera il codice Bash:

$ false
$ echo $?
1

"C" equivalente è:

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));
}

Compila ed esegui:

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

Produzione:

$? = 1

In Bash, quando premi Invio, un fork + exec + wait avviene come sopra, e bash quindi imposta $?sullo stato di uscita del processo biforcato.

Nota: per comandi integrati come echo, non è necessario generare un processo e Bash si limita $?a 0 per simulare un processo esterno.

Standard e documentazione

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

? Si espande allo stato di uscita decimale della pipeline più recente (consultare Pipeline).

man bash "Parametri speciali":

La shell tratta diversi parametri appositamente. Questi parametri possono solo essere referenziati; l'assegnazione a loro non è consentita. [...]

? Si espande allo stato di uscita della pipeline in primo piano eseguita più di recente.

ANSI C e POSIX raccomandano quindi di:

  • 0 significa che il programma ha avuto successo

  • altri valori: il programma è fallito in qualche modo.

    Il valore esatto potrebbe indicare il tipo di errore.

    ANSI C non definisce il significato di alcun valore e POSIX specifica valori maggiori di 125: Qual è il significato di "POSIX"?

Bash utilizza lo stato di uscita per if

In Bash, usiamo spesso $?implicitamente lo stato di uscita per controllare le ifistruzioni come in:

if true; then
  :
fi

dove trueè un programma che restituisce solo 0.

Quanto sopra equivale a:

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

E in:

if [ 1 = 1 ]; then
  :
fi

[è solo un programma con un nome strano (e incorporato in Bash che si comporta in modo simile), e i 1 = 1 ]suoi argomenti, vedi anche: Differenza tra parentesi quadre singole e doppie in Bash




2

Emette il risultato dell'ultimo comando unix eseguito

0 implies true
1 implies false
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.