Significado de $? (signo de interrogación en dólares) en scripts de shell


Respuestas:


212

Este es el estado de salida del último comando ejecutado.

Por ejemplo, el comando truesiempre devuelve un estado de 0y falsesiempre devuelve un estado de 1:

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

Del manual: (accesible llamando a man bash en su shell)

$?       Se expande al estado de salida de la tubería de primer plano ejecutada más recientemente.

Por convención, un estado de salida 0significa éxito, y un estado de retorno distinto de cero significa falla. Obtenga más información sobre los estados de salida en wikipedia .

Hay otras variables especiales como esta, como puede ver en este manual en línea: https://www.gnu.org/s/bash/manual/bash.html#Special-Parameters


Tenga en cuenta $y ?son dos parámetros distintos y $?no aparece en la página de manual de bash (1).
Josh Habdas

19

$?devuelve el valor de salida del último comando ejecutado. echo $?imprime ese valor en la consola. cero implica una ejecución exitosa, mientras que los valores distintos de cero se asignan a varios motivos de falla.

Por lo tanto, al crear scripts; Tiendo a usar la siguiente sintaxis

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

La comparación se debe hacer en iguales 0o no iguales 0.

** Actualización basada en el comentario: Idealmente, no debe usar el bloque de código anterior para la comparación, consulte los comentarios y la explicación de @tripleee.


15
No, esto es un antipatrón. Cualquier cosa que parezca cmd; if [ $? -eq 0 ]; thendebería ser refactorizada if cmd; then. El propósito mismo de if(y las otras declaraciones de control de flujo en el shell) es ejecutar un comando y examinar su estado de salida.
tripleee

if cmd;puede no ser muy legible en algunas condiciones, especialmente cuando cmd se refiere a otro script.
Saurabh Ariyan

1
Esto está aún más mal ahora. [ 1 ]y [ 0 ]son ambos verdaderos; [sin un operador comprueba si el argumento es una cadena no vacía.
tripleee

2
Estoy a punto de hacerlo vendor/bin/drush status bootstrap | grep -q $(vendor/bin/drush php-eval 'if (function_exists("t")) echo t("Successful");') &> /dev/null;. Si tuviera que poner eso en una sola línea if [ ... ], sería terriblemente ilegible. Planeo almacenar la salida de esa línea en una variable para poder decir if [ $drupal_installed -eq 0 ]más tarde.
Thirdender

1
@thirdender La solución adecuada para eso es encapsular la prueba compleja en una función de shell.
tripleee el

12

echo $? - Da el ESTADO DE SALIDA del comando ejecutado más recientemente . Este ESTADO DE SALIDA probablemente sea un número con CERO que implique Éxito y cualquier valor NO CERO que indique Falla

? - Este es un parámetro / variable especial en bash.

PS - Da el valor almacenado en la variable "?".

Algunos parámetros especiales similares en BASH son 1,2, *, # (normalmente visto en el comando echo como $ 1, $ 2, $ *, $ #, etc.,).



5

Ejemplo de estado de salida mínimo de POSIX C

Para comprender $?, primero debe comprender el concepto de estado de salida del proceso que define POSIX . En Linux:

  • cuando un proceso llama a la exitllamada del sistema, el núcleo almacena el valor pasado a la llamada del sistema (unint ) incluso después de que el proceso falle.

    La exit()función ANSI C llama a la llamada del sistema de salida , e indirectamente cuando lo hace returndesde main.

  • El proceso que llamó al proceso secundario existente (Bash), a menudo con fork+ exec, puede recuperar el estado de salida del elemento secundario con la waitllamada al sistema

Considere el código Bash:

$ false
$ echo $?
1

El "equivalente" C es:

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

Compilar y ejecutar:

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

Salida:

$? = 1

En Bash, cuando presiona enter, se produce una bifurcación + exec + espera como arriba, y bash luego establece $?el estado de salida del proceso bifurcado.

Nota: para comandos integrados como echo, un proceso no necesita ser generado, y Bash solo establece$? en 0 para simular un proceso externo.

Estándares y documentación

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

? Se expande al estado de salida decimal de la canalización más reciente (consulte Canalizaciones).

man bash "Parámetros especiales":

El shell trata varios parámetros especialmente. Estos parámetros solo pueden ser referenciados; la asignación a ellos no está permitida. [...]

? Se expande al estado de salida de la canalización en primer plano ejecutada más recientemente.

ANSI C y POSIX luego recomiendan que:

  • 0 significa que el programa fue exitoso

  • otros valores: el programa falló de alguna manera.

    El valor exacto podría indicar el tipo de falla.

    ANSI C no define el significado de ningún valor, y POSIX especifica valores mayores que 125: ¿Cuál es el significado de "POSIX"?

Bash usa el estado de salida para if

En Bash, a menudo usamos el estado de salida $?implícitamente para controlar las ifdeclaraciones como en:

if true; then
  :
fi

donde truees un programa que solo devuelve 0.

Lo anterior es equivalente a:

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

Y en:

if [ 1 = 1 ]; then
  :
fi

[es solo un programa con un nombre extraño (y Bash incorporado que se comporta como tal), y 1 = 1 ]sus argumentos, vea también: Diferencia entre corchetes simples y dobles en Bash




2

Emite el resultado del último comando unix ejecutado

0 implies true
1 implies false
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.