Significado de $? (ponto de interrogação em dólar) em scripts de shell


Respostas:


212

Este é o status de saída do último comando executado.

Por exemplo, o comando truesempre retorna um status de 0e falsesempre retorna um status de 1:

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

Do manual: (acessível chamando man bashno seu shell)

$?       Expande para o status de saída do pipeline de primeiro plano executado mais recentemente.

Por convenção, um status de saída 0significa sucesso e status de retorno diferente de zero significa falha. Saiba mais sobre os status de saída na wikipedia .

Existem outras variáveis ​​especiais como essa, como você pode ver neste manual on-line: https://www.gnu.org/s/bash/manual/bash.html#Special-Parameters


Observe $e ?são dois parâmetros distintos e $?não aparece na página de manual do bash (1).
Josh Habdas

19

$?retorna o valor de saída do último comando executado. echo $?imprime esse valor no console. zero implica uma execução bem-sucedida, enquanto valores diferentes de zero são mapeados para vários motivos de falha.

Portanto, ao criar scripts; Costumo usar a seguinte sintaxe

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

A comparação deve ser feita em iguais a 0ou não 0.

** Atualização com base no comentário: idealmente, você não deve usar o bloco de código acima para comparação, consulte os comentários e explicações do @tripleee.


15
Não, isso é um antipadrão. Qualquer coisa que pareça cmd; if [ $? -eq 0 ]; thendeve ser refatorada para if cmd; then. O próprio propósito de if(e as outras instruções de controle de fluxo no shell) é para executar um comando e examinar seu estado de saída.
Tripleee 15/10

if cmd;pode não ser muito legível em algumas condições, especialmente quando cmd se refere a outro script.
Saurabh Ariyan

1
Isso está ainda mais errado agora. [ 1 ]e [ 0 ]são ambos verdadeiros; [sem um operador verifica se o argumento é uma sequência não vazia.
tripleee

2
Estou prestes a fazer vendor/bin/drush status bootstrap | grep -q $(vendor/bin/drush php-eval 'if (function_exists("t")) echo t("Successful");') &> /dev/null;. Se eu tivesse que colocar isso em uma única linha if [ ... ], seria terrivelmente ilegível. Eu pretendo armazenar a saída dessa linha em uma variável, para que eu possa dizer if [ $drupal_installed -eq 0 ]mais tarde.
junender

1
@ thirdender A solução adequada para isso é encapsular o teste complexo em uma função shell.
tripleee

12

eco $? - Dá o EXIT STATUS do comando executado mais recentemente . Esse EXIT STATUS provavelmente seria um número com ZERO implicando Success e qualquer valor NÃO-ZERO indicando Failure

? - Este é um parâmetro / variável especial no bash.

$? - Fornece o valor armazenado na variável "?".

Alguns parâmetros especiais semelhantes no BASH são 1,2, *, # (normalmente vistos no comando echo como $ 1, $ 2, $ *, $ #, etc.).



5

Exemplo mínimo de status de saída do POSIX C

Para entender $?, você deve primeiro entender o conceito de status de saída do processo definido pelo POSIX . No Linux:

  • quando um processo chama a chamada do exitsistema, o kernel armazena o valor passado para a chamada do sistema (an int) mesmo depois que o processo morre.

    A chamada de sistema exit é chamada pela exit()função ANSI C e, indiretamente, quando você faz returna partir main.

  • o processo que chamou o processo filho de saída (Bash), geralmente com fork+ exec, pode recuperar o status de saída do filho com a waitchamada do sistema

Considere o código Bash:

$ false
$ echo $?
1

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

Compile e execute:

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

Resultado:

$? = 1

No Bash, quando você pressiona enter, um fork + exec + wait acontece como acima, e o bash define $?o status de saída do processo bifurcado.

Nota: para comandos internos echo, como , um processo não precisa ser gerado, e o Bash apenas define $?como 0 para simular um processo externo.

Normas e documentação

POSIX 7 2.5.2 "Parâmetros especiais" http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_05_02 :

? Expande para o status de saída decimal do pipeline mais recente (consulte Pipelines).

man bash "Parâmetros especiais":

O shell trata vários parâmetros especialmente. Esses parâmetros podem ser referenciados apenas; a atribuição a eles não é permitida. [...]

? Expande para o status de saída do pipeline de primeiro plano executado mais recentemente.

O ANSI C e o POSIX recomendam que:

  • 0 significa que o programa foi bem sucedido

  • outros valores: o programa falhou de alguma forma.

    O valor exato pode indicar o tipo de falha.

    O ANSI C não define o significado de quaisquer valores e o POSIX especifica valores maiores que 125: Qual é o significado de "POSIX"?

O Bash usa o status de saída para if

No Bash, geralmente usamos o status de saída $?implicitamente para controlar ifinstruções como em:

if true; then
  :
fi

Onde trueé um programa que apenas retorna 0.

O acima é equivalente a:

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

E em:

if [ 1 = 1 ]; then
  :
fi

[é apenas um programa com um nome estranho (e o Bash incorporado que se comporta como ele) e 1 = 1 ]seus argumentos, veja também: Diferença entre colchetes simples e duplos no Bash




2

Mostra o resultado do último comando unix executado

0 implies true
1 implies false
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.