Значение $? (знак вопроса доллара) в сценариях оболочки


Ответы:


212

Это статус выхода последней выполненной команды.

Например, команда trueвсегда возвращает статус 0и falseвсегда возвращает статус 1:

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

Из руководства: (доступно через вызов man bashв вашей оболочке)

$?       Расширяется до состояния выхода последнего выполненного переднего конвейера.

По соглашению статус выхода 0означает успех, а ненулевой статус возврата означает сбой. Узнайте больше о статусах выхода в Википедии .

Есть и другие специальные переменные, подобные этой, как вы можете видеть в этом онлайн-руководстве: https://www.gnu.org/s/bash/manual/bash.html#Special-Parameters


Обратите внимание, $и ?это два разных параметра $?, которые не отображаются на странице руководства bash (1).
Джош Хабдас

19

$?возвращает значение выхода последней выполненной команды. echo $?выводит это значение на консоль Ноль подразумевает успешное выполнение, в то время как ненулевые значения отображаются на различные причины сбоя.

Следовательно, при написании сценариев; Я склонен использовать следующий синтаксис

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

Сравнение должно быть сделано на равных 0или не равных 0.

** Обновление на основе комментария: в идеале, вы не должны использовать вышеуказанный блок кода для сравнения, обратитесь к комментариям и объяснениям @tripleee.


15
Нет, это антипаттерн. Все, на что похоже, cmd; if [ $? -eq 0 ]; thenдолжно быть подвергнуто рефакторингу if cmd; then. Сама цель из if(и другие операторы управления потоком в оболочке), чтобы выполнить команду и проверьте его состояние выхода.
tripleee

if cmd;Некоторые условия могут быть не очень читабельными, особенно когда cmd ссылается на другой скрипт.
Саураая Арья

1
Это еще больше неправильно сейчас. [ 1 ]и [ 0 ]оба верны; [без оператора проверяет, является ли аргумент непустой строкой.
tripleee

2
Я собираюсь сделать vendor/bin/drush status bootstrap | grep -q $(vendor/bin/drush php-eval 'if (function_exists("t")) echo t("Successful");') &> /dev/null;. Если бы мне пришлось поместить это в одну строку, if [ ... ]это было бы ужасно нечитаемо. Я планирую сохранить вывод этой строки в переменной, так что я могу сказать if [ $drupal_installed -eq 0 ]позже.
Третий

1
@thirdender Правильное решение этого заключается в том, чтобы заключить сложный тест в функцию оболочки.
Трипли

12

echo $? - Дает СТАТУС ВЫХОДА последней выполненной команды . Это СОСТОЯНИЕ ВЫХОДА, скорее всего, будет числом с НУЛЕМ, означающим Успех, и любым НУЛЕВОЙ величиной, указывающей Отказ

? - Это один специальный параметр / переменная в bash.

$? - Это дает значение, хранящееся в переменной «?».

Некоторые аналогичные специальные параметры в BASH: 1,2, *, # (обычно в команде echo видны как $ 1, $ 2, $ *, $ # и т. Д.,).



5

Пример минимального состояния выхода POSIX C

Чтобы понять $?, вы должны сначала понять концепцию статуса выхода процесса, которая определяется POSIX . В Linux:

  • когда процесс вызывает exitсистемный вызов, ядро ​​сохраняет значение, переданное системному вызову (int ), даже после того, как процесс умирает.

    Системный вызов выхода вызывается exit()функцией ANSI C, и косвенно, когда вы делаете returnиз main.

  • процесс, который вызвал выходящий дочерний процесс (Bash), часто с fork+ exec, может получить статус выхода дочернего процесса с помощью waitсистемного вызова

Рассмотрим код Bash:

$ false
$ echo $?
1

C «эквивалент» это:

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

Скомпилируйте и запустите:

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

Вывод:

$? = 1

В Bash, когда вы нажимаете Enter, происходит форк + exec + wait, как описано выше, а затем bash устанавливает $?состояние завершения разветвленного процесса.

Примечание: для встроенных команд, таких как echo, процесс не нужно запускать, а Bash просто устанавливает$? 0 для имитации внешнего процесса.

Стандарты и документация

POSIX 7 2.5.2 «Специальные параметры» http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_05_02 :

? Расширяется до десятичного состояния выхода самого последнего конвейера (см. Конвейеры).

man bash «Специальные параметры»:

Оболочка обрабатывает несколько параметров специально. На эти параметры можно ссылаться только; присвоение им не допускается. [...]

? Расширяется до состояния выхода последнего выполненного переднего конвейера.

Затем ANSI C и POSIX рекомендуют:

  • 0 означает, что программа была успешной

  • другие значения: программа как-то не удалась.

    Точное значение может указывать тип отказа.

    ANSI C не определяет значение каких-либо значений, а POSIX определяет значения больше 125: что означает «POSIX»?

Bash использует статус выхода для if

В Bash мы часто $?неявно используем статус выхода для управления ifоператорами, как в:

if true; then
  :
fi

где trueпрограмма, которая просто возвращает 0.

Вышеуказанное эквивалентно:

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

И в:

if [ 1 = 1 ]; then
  :
fi

[это просто программа со странным именем (и встроенной в Bash, которая ведет себя так же, как она) и 1 = 1 ]ее аргументами, см. также: Разница между одинарными и двойными квадратными скобками в Bash



3

См . Руководство по Bash в разделе 3.4.2. Специальные параметры :

? - Расширяется до состояния выхода последнего выполненного переднего конвейера.

Это немного сложно найти, потому что он не указан как $?(имя переменной «просто» ?). Также смотрите раздел статуса выхода , конечно ;-)

Удачного кодирования.


Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.