Znaczenie $? (znak zapytania dolara) w skryptach powłoki


Odpowiedzi:


212

To jest kod zakończenia ostatniego wykonanego polecenia.

Na przykład polecenie truezawsze zwraca stan 0i falsezawsze zwraca stan 1:

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

Z instrukcji: (dostępne po wywołaniu man bashpowłoki)

$?       Rozwija się do statusu wyjścia ostatnio wykonanego potoku pierwszego planu.

Zgodnie z konwencją status wyjścia 0oznacza sukces, a niezerowy status powrotu oznacza niepowodzenie. Dowiedz się więcej o statusach wyjścia na Wikipedii .

Istnieją inne zmienne specjalne, takie jak ta, jak widać w tym podręczniku online: https://www.gnu.org/s/bash/manual/bash.html#Special-Parameters


Uwaga $i ?są to dwa różne parametry $?, które nie pojawiają się na stronie podręcznika bash (1).
Josh Habdas

19

$?zwraca wartość wyjściową ostatnio wykonanego polecenia. echo $?wypisuje tę wartość na konsoli. zero oznacza pomyślne wykonanie, podczas gdy wartości niezerowe są mapowane na różne przyczyny niepowodzenia.

Dlatego podczas tworzenia skryptów; Zwykle używam następującej składni

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

Porównanie należy przeprowadzić na równych 0lub nie równych 0.

** Aktualizacja Na podstawie komentarza: Idealnie byłoby, gdybyś nie używał powyższego bloku kodu do porównania, zapoznaj się z komentarzami i wyjaśnieniami @tripleee.


15
Nie, to jest anty-wzór. Wszystko, co wygląda, cmd; if [ $? -eq 0 ]; thenpowinno zostać zmienione if cmd; then. Sam cel od if(i inne instrukcje sterujące przepływem w skorupkach) jest uruchomienie komendy i zbadać jej stan wyjścia.
tripleee

if cmd;niektóre warunki mogą nie być zbyt czytelne, zwłaszcza gdy cmd odnosi się do innego skryptu.
Saurabh Ariyan

1
Teraz jest to jeszcze bardziej błędne. [ 1 ]i [ 0 ]oba są prawdziwe; [bez operatora sprawdza, czy argument jest niepustym łańcuchem.
tripleee

2
Mam zamiar zrobić vendor/bin/drush status bootstrap | grep -q $(vendor/bin/drush php-eval 'if (function_exists("t")) echo t("Successful");') &> /dev/null;. Gdybym miał to umieścić w jednym wierszu if [ ... ], byłoby to strasznie nieczytelne. Planuję zapisać wynik tej linii w zmiennej, więc mogę tylko powiedzieć if [ $drupal_installed -eq 0 ]później.
trzeci zawodnik

1
@thirdender Właściwym rozwiązaniem jest zamknięcie złożonego testu w funkcji powłoki.
tripleee

12

echo $? - Podaje STATUS WYJŚCIA ostatnio wykonanego polecenia . Ten STATUS WYJŚCIA najprawdopodobniej byłby liczbą z ZEREM oznaczającym sukces i jakąkolwiek wartością NIĄZ ZEROWĄ oznaczającą niepowodzenie

? - To jest jeden specjalny parametr / zmienna w bash.

$? - Podaje wartość przechowywaną w zmiennej „?”.

Niektóre podobne parametry specjalne w BASH to 1,2, *, # (zwykle widoczne w poleceniu echo jako $ 1, $ 2, $ *, $ #, itd.,).



5

Przykład statusu wyjścia minimalnego POSIX C.

Aby to zrozumieć $?, musisz najpierw zrozumieć koncepcję statusu zakończenia procesu, która jest zdefiniowana w POSIX . W systemie Linux:

  • kiedy proces wywołuje exitwywołanie systemowe, jądro przechowuje wartość przekazaną do wywołania systemowego (a int) nawet po zakończeniu procesu.

    Wywołanie systemowe wyjścia jest wywoływane przez funkcję exit()ANSI C i pośrednio, gdy robisz returnfrom main.

  • proces, który wywołał wychodzący proces potomny (Bash), często z fork+ exec, może pobrać status wyjścia dziecka za pomocą waitwywołania systemowego

Rozważ kod Bash:

$ false
$ echo $?
1

„Odpowiednik” języka C to:

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

Skompiluj i uruchom:

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

Wynik:

$? = 1

W Bash, kiedy wciśniesz enter, następuje fork + exec + wait, jak powyżej, a następnie bash ustawia $?status zakończenia rozwidlonego procesu.

Uwaga: w przypadku poleceń wbudowanych, takich jak echoproces, nie musi być spawnowany, a Bash ustawia tylko $?0, aby symulować proces zewnętrzny.

Normy i dokumentacja

POSIX 7 2.5.2 „Parametry specjalne” http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_05_02 :

? Rozwija się do dziesiętnego statusu wyjścia najnowszego potoku (zobacz Potoki).

man bash „Parametry specjalne”:

Powłoka traktuje specjalnie kilka parametrów. Te parametry mogą być tylko przywoływane; przypisywanie do nich nie jest dozwolone. […]

? Rozwija się do statusu wyjścia ostatnio wykonanego potoku pierwszego planu.

ANSI C i POSIX zalecają, aby:

  • 0 oznacza, że ​​program się powiódł

  • inne wartości: program w jakiś sposób zawiódł.

    Dokładna wartość może wskazywać rodzaj awarii.

    ANSI C nie definiuje znaczenia żadnych zmiennych, a POSIX określa wartości większe niż 125: Jakie jest znaczenie „POSIX”?

Bash używa statusu wyjścia dla if

W Bash często $?niejawnie używamy statusu wyjścia do kontrolowania ifinstrukcji, jak w:

if true; then
  :
fi

gdzie truejest programem, który po prostu zwraca 0.

Powyższe jest równoważne z:

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

I w:

if [ 1 = 1 ]; then
  :
fi

[to tylko program o dziwnej nazwie (i wbudowanym Bash, który zachowuje się tak jak on) i 1 = 1 ]jego argumenty, zobacz także: Różnica między pojedynczymi i podwójnymi nawiasami kwadratowymi w Bash




2

Wyprowadza wynik ostatniego wykonanego polecenia unixowego

0 implies true
1 implies false
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.