¿Por qué 0 es verdadero pero falso es 1 en el shell?

122
false; echo $?

Lo anterior saldrá 1, que es contradictorio con todos los demás lenguajes de programación que conozco.

¿Alguna razón en esto?

httpinterpretar
fuente
3
También está en línea con Unix Way ... no devuelva nada en caso de éxito.
Abdullah Jibaly
21
Porque un estado de salida no es booleano. Simple como eso.
Jens
2
Tenga en cuenta que falseno es un booleano, como en otros lenguajes de programación. Es solo un programa ubicado en /bin/false( /usr/bin/falseen una Mac) que siempre debe devolver un código de salida de error 1. Similar para true. Así que aquí no existe el casting. Se trata solo de códigos de salida.
Mikhail Vasin
Un shell es una interfaz (de usuario) para un sistema operativo. Los programas Unix, etc. siguen la convención de salir con 0 como OK, por la razón dada en las respuestas (capacidad para comunicar más de una razón de falla). El shell simplemente mantiene esta convención, lo que hace que las construcciones sean if myprog; then echo OK; fifáciles e intuitivas. De lo contrario, tendría que invertir todas las pruebas para que un programa tenga éxito.
Peter - Reincorpora a Monica

Respuestas:

93

Es una convención, pero particularmente útil cuando lo piensas. En general, si un programa tiene éxito, eso es todo lo que necesita saber. Sin embargo, si falla, es posible que deba conocer todo tipo de información sobre la falla: por qué sucedió, cómo solucionarlo, etc. Tener un 'éxito' de media cero y una falla de media distinta de cero le permite verificar el éxito con bastante facilidad e investigue el error en particular para obtener más detalles si lo desea. Muchas API y marcos tienen una convención similar: las funciones que tienen éxito devuelven 0 y las que fallan devuelven un código de error que describe el caso de falla en particular.

Carl Norum
fuente
6
Entiendo esta respuesta, pero todavía no entiendo por qué la conversión de bool a int está invertida. ¿Existe una "convención booleana de retorno" que diga: si devuelve verdadero, significa que no hay errores, si devuelve falso, significa que se produjo un error (como la "convención de código de error = entero" que es más conocida)?
Guillaume86
1
Asumir que hay una conversión de booleano a int implica que realmente no entendió la respuesta. Cero significa éxito y todos los de 1, 2, ..., 255 son códigos de error que pueden comunicar de manera útil diferentes escenarios de falla. Un ejemplo particular es el xargsque usa diferentes códigos de error en el rango alrededor de 127 para indicar cómo falló un grupo de comandos. La conversión que se puede hacer es de int a bool donde 0 se asigna al éxito (que supongo que desea expresar como verdadero / 1; pero tenga en cuenta que esto es solo otra convención arbitraria) y todos los demás valores al fracaso.
tripleee
79

Bash es un lenguaje de programación (scripting), pero también es un shell y una interfaz de usuario. Si0 fue un error, entonces el programa solo podría presentar un tipo de error.

Sin embargo, en Bash, cualquier valor distinto de cero es un error y podemos usar cualquier número del 1 al 255 para representar un error. Esto significa que podemos tener muchos tipos diferentes de errores. 1es un error general, 126significa que no se puede ejecutar un archivo, 127significa 'comando no encontrado', etc. Aquí hay una lista de códigos de salida Bash con significados especiales que muestran algunos de los códigos de salida más comunes.

También hay muchos tipos de éxito (el estado de salida es 0). Sin embargo, un éxito le permitirá continuar con el siguiente paso: puede imprimir los resultados en una pantalla o ejecutar un comando, etc.

Stefan Lasiewski
fuente
8
Esta respuesta pragmática que muestra la utilidad de una variedad de códigos de retorno es preferible a la predicación de algunas de las otras respuestas.
javadba
1
Y solo por el gusto de hacerlo, señalaré que las /usr/include/sysexits.hnotas salen de valores que quizás sean más aspiracionales, aunque la convención que representan se remonta a la década de 1980. Esta convención existe fuera del alcance de bash.
ghoti
2
Una explicación genial, sencilla y pragmática. Esto debe estar en la parte superior.
Rey Leonard Amorato
3
"Si 0fue un error, entonces el programa solo podría presentar un tipo de error" . Esta afirmación es clave y la razón por la que esta respuesta debería estar en la parte superior.
rayryeng
1
@LuisLavaire Ese es un comportamiento indefinido. En la práctica, el número tiende a truncarse; pero definitivamente no sería "más incorrecto" si el tiempo de ejecución generara una advertencia y / o simplemente fallara. El sistema operativo reservó exactamente un byte para esta información y al intentar poner más de un byte, definitivamente hay un error. Pero los diseñadores del sistema operativo probablemente se encontraron con fallas el primer día, se encogieron de hombros y decidieron que lo mejor que pueden hacer es simplemente truncar el valor y seguir adelante.
tripleee
30

Aquí hay dos cuestiones relacionadas.

Primero, la pregunta del OP, ¿Por qué 0 es verdadero pero falso es 1 en el caparazón? y el segundo, ¿por qué las aplicaciones devuelven 0 para el éxito y no cero para el fracaso?

Para responder a la pregunta del OP, debemos comprender la segunda pregunta. Las numerosas respuestas a esta publicación han descrito que se trata de una convención y han enumerado algunas de las sutilezas que ofrece esta convención. Algunas de estas sutilezas se resumen a continuación.

¿Por qué las aplicaciones devuelven 0 para el éxito y no cero para el fracaso?

El código que invoca una operación necesita saber dos cosas sobre el estado de salida de la operación. ¿Salió la operación con éxito? [* 1] Y si la operación no se cierra correctamente, ¿por qué la operación se cierra sin éxito? Se puede utilizar cualquier valor para indicar éxito. Pero 0 es más conveniente que cualquier otro número porque es portátil entre plataformas. Resumiendo la respuesta de xibo a esta pregunta el 16 de agosto de 2011:

Zero es independiente de la codificación.

Si quisiéramos almacenar uno (1) en una palabra entera de 32 bits, la primera pregunta sería "¿palabra big-endian o palabra little-endian?", Seguida de "¿cuánto tiempo componen los bytes una palabra little-endian?" ", mientras que cero siempre se verá igual.

También es de esperar que algunas personas lancen errno a char o short en algún momento, o incluso a flotar. (int) ((char) ENOLCK) no es ENOLCK cuando char no tiene al menos 8 bits de longitud (las máquinas de caracteres ASCII de 7 bits son compatibles con UNIX), mientras que (int) ((char) 0) es 0 independiente del detalles arquitectonicos de char.

Una vez que se determina que 0 será el valor de retorno para el éxito, entonces tiene sentido usar cualquier valor distinto de cero para el fracaso. Esto permite que muchos códigos de salida respondan a la pregunta de por qué falló la operación.

¿Por qué 0 es verdadero pero falso es 1 en el shell?

Uno de los usos fundamentales de los shells es automatizar procesos mediante secuencias de comandos. Por lo general, esto significa invocar una operación y luego hacer otra cosa de manera condicional en función del estado de salida de la operación. Philippe A. explicó muy bien en su respuesta a esta publicación que

En bash y en shells de Unix en general, los valores de retorno no son booleanos. Son códigos de salida enteros.

Entonces es necesario interpretar el estado de salida de estas operaciones como un valor booleano. Tiene sentido asignar un 0estado de salida exitoso ( ) a verdadero y cualquier estado de salida distinto de cero / error a falso. Hacer esto permite la ejecución condicional de comandos de shell encadenados.

He aquí un ejemplo mkdir deleteme && cd $_ && pwd. Debido a que el shell interpreta 0 como verdadero, este comando funciona convenientemente como se esperaba. Si el shell interpretara 0 como falso, entonces tendría que invertir el estado de salida interpretado para cada operación.

En resumen, no tendría sentido que el shell interpretara 0 como falso dada la convención de que las aplicaciones devuelven 0 para un estado de salida exitoso.


[* 1]: Sí, muchas veces las operaciones necesitan devolver más que un simple mensaje de éxito, pero eso está más allá del alcance de este hilo.

Consulte también el Apéndice E en la Guía avanzada de secuencias de comandos Bash

axiopistía
fuente
2
Hola, Solo para enfatizar que, si el shell interpretara cero como falso y no cero como verdadero, el truco de hacerlo mkdir deleteme && cd _$ && pwdno fallaría realmente; pero tendríamos que reemplazarlo por mkdir deleteme || cd _$ || pwd, que en mi opinión es mucho menos claro, porque lo que realmente queremos hacer es mkdir deletemeycd _$ypwd... (donde “y” tiene aquí su significado del lenguaje ordinario).
Rémi Peyre
1
Creo que cubrí eso en mi respuesta. Sin embargo, para ser claros, cuando se invierte la lógica, no es suficiente reemplazar los &&operadores por ||operadores. Debería aplicar completamente la ley de De Morgan. Ver: en.wikipedia.org/wiki/De_Morgan%27s_laws
axiopisty
1
Otra consideración: veo al menos tres razones por las que, en términos generales, sería natural afirmar que truecorresponde a cero y falsecorresponde a distinto de cero. Primero, si te digo algo, “haberte dicho la verdad” depende de la cantidad de mentiras que he dicho: o es cero y he dicho (globalmente) la verdad, o es diferente de cero y he mentido. Esto es esencialmente lo mismo que querer que lo lógico se andcorresponda con el “y” común de la suma (cuando se restringe a números naturales, obviamente).
Rémi Peyre
1
(continúa del comentario anterior). La segunda razón es que hay una verdad pero muchas no verdades; por lo tanto, es más conveniente asociar un valor único para truey todos los demás valores para false. (Esto es esencialmente lo mismo que las consideraciones sobre los valores de retorno de los programas).
Rémi Peyre
(continúa del comentario anterior). La tercera razón es que "verdadero que verdadero que A" es lo mismo que "verdadero que A", "falso que falso que A" es lo mismo que "falso que A", etc .; por lo que se truecomporta como el multiplicativo 1 y falsecomo el multiplicativo -1. Lo cual, como potencias de -1, significa que trueademás se comporta como "par" y falsecomo "impar". De nuevo, es el trueque merece ser cero ...
Rémi Peyre
17

El único punto fundamental que encuentro importante de entender es este. En bash y en shells de Unix en general, los valores de retorno no son booleanos. Son códigos de salida enteros. Como tal, debe evaluarlos de acuerdo con la convención que dice que 0 significa éxito y otros valores significan algún error.

Con test, [ ]o [[ ]]los operadores, las condiciones de bash evalúan como cierto en el caso de un código de salida de 0 (el resultado de / bin / true). De lo contrario, se evalúan como falsos.

Las cadenas se evalúan de manera diferente a los códigos de salida:

if [ 0 ] ; then echo not null ; fi
if [ $(echo 0) ] ; then echo not null ; fi

if [ -z "" ] ; then echo null ; fi

El (( ))operador aritmético interpreta 1 y 0 como verdadero y falso. Pero ese operador no puede ser utilizado como un sustituto completo para test, [ ]o [[ ]]. A continuación, se muestra un ejemplo que muestra cuándo es útil el operador aritmético:

for (( counter = 0 ; counter < 10 ; counter ++ )) ; do
  if (( counter % 2 )) ; then echo "odd number $counter" ; fi
done
Philippe A.
fuente
Gracias por la explicación de las llaves vs paréntesis en verdadero / falso
javadba
15

Es solo una convención que un código de salida 0 significa éxito. EXIT_SUCCESS será 0 en casi todos los sistemas modernos.

EDITAR:

"¿Por qué tanto la prueba 0 como la prueba 1 devuelven 0 (éxito)?"

Esa es una pregunta completamente diferente. La respuesta es que pasar un solo argumento para probar siempre resulta exitoso a menos que ese argumento sea la cadena nula (""). Consulte la documentación de Open Group .

Matthew Flaschen
fuente
Entonces, ¿por qué ambos test 0y test 1devuelve 0 (éxito)?
httpinterpret
5
@httpinterpret, testno prueba valores numéricos. Comprueba si esa cadena es o no la cadena nula. man testpara más información.
Carl Norum
12

Normalmente, los programas devuelven cero para el éxito, distinto de cero para el fracaso; falsedevuelve 1 porque es un valor conveniente distinto de cero, pero generalmente cualquier valor distinto de cero significa falla de algún tipo, y muchos programas devolverán diferentes valores distintos de cero para indicar diferentes modos de falla

Michael Mrozek
fuente
2
Los votos negativos sin explicación (o razón obvia) apestan en absoluto y los que lo hacen son cojos porque no están ayudando a la comunidad en absoluto.
Abdullah Jibaly
1
No lo tolero ... pero son sus 2 puntos ... y @MichaelMrozek ... con 19.6k, no puede doler demasiado, jajaja.
Alex Gray
1
@alexgray Esto fue hace dos años; en ese momento tenía unos 5k. Y tenía más curiosidad por saber qué estaba mal con la respuesta que con el representante
Michael Mrozek
4

es una convención que se remonta a los primeros días de Unix.

Por convención, todas las llamadas al sistema devuelven 0 si tienen éxito, distinto de cero en caso contrario, porque entonces se pueden usar diferentes números para indicar diferentes razones de falla.

Los shells siguen esta convención, 0 significa que el último comando se realizó correctamente, distinto de cero en caso contrario. De manera similar, el valor de retorno distinto de cero es útil para los mensajes de error de salida: por ejemplo, 1: "muerte cerebral", 2: "desalmado", etc.


fuente
Esta es la respuesta.
Peter - Restablece a Monica
3

Estás tratando de equiparar verdadero / falso con éxito / fracaso.

¡Son dos dicotomías completamente, aunque sutilmente al principio, diferentes!

En las secuencias de comandos de shell, no existe tal cosa como verdadero / falso. Las 'expresiones' de Shell no se interpretan como verdadero / falso. Más bien, las 'expresiones' de shell son procesos que tienen éxito o fallan.

Obviamente, un proceso puede fallar por muchas razones. Por lo tanto, necesitamos un conjunto de códigos más grande para asignar posibles fallas. Los números enteros positivos funcionan. Por otro lado, si el proceso tiene éxito, eso significa que hizo exactamente lo que se suponía que debía hacer. Dado que solo hay una forma de hacerlo, solo necesitamos un código. 0 hace el truco.

En C, estamos creando un programa. En un script de shell, estamos ejecutando un montón de programas para hacer algo.

¡Diferencia!

capitánincurrie
fuente
Es confuso. Escriba 'hombre ['. Muestra La utilidad de prueba evalúa la expresión y, si se evalúa como verdadera, devuelve un estado de salida cero (verdadero); de lo contrario, devuelve 1 (falso). Si no hay expresión, la prueba también devuelve 1 (falso).
cabalgata
1

Quizás una buena forma de recordarlo es:

  • Los códigos de retorno responden "¿cuál es la respuesta?" verdadero (u otro resultado) o falso
  • Los códigos de salida responden "¿cuál es el problema?" código de salida o sin problema (0)
jjisnow
fuente