¿Cómo puedo declarar y utilizar variables booleanas en un script de shell?
Intenté declarar una variable booleana en un script de shell usando la siguiente sintaxis:
variable=$false
variable=$true
¿Es esto correcto? Además, si quisiera actualizar esa variable, ¿usaría la misma sintaxis? Finalmente, ¿es correcta la siguiente sintaxis para usar variables booleanas como expresiones?
if [ $variable ]
if [ !$variable ]
Respuesta revisada (12 de febrero de 2014)
the_world_is_flat=true
# ...do something interesting...
if [ "$the_world_is_flat" = true ] ; then
echo 'Be careful not to fall off!'
fi
Respuesta original
Advertencias: https://stackoverflow.com/a/21210966/89391
the_world_is_flat=true
# ...do something interesting...
if $the_world_is_flat ; then
echo 'Be careful not to fall off!'
fi
De: Usar variables booleanas en Bash
La razón por la que la respuesta original se incluye aquí es porque los comentarios anteriores a la revisión del 12 de febrero de 2014 pertenecen únicamente a la respuesta original, y muchos de los comentarios son incorrectos cuando se asocian con la respuesta revisada. Por ejemplo, el comentario de Dennis Williamson sobre bash incorporado true
el 2 de junio de 2010 solo se aplica a la respuesta original, no a la revisada.
TL;DR
my_bool=true
if [ "$my_bool" = true ]
Problemas con la respuesta de Miku ( original )
No recomiendo la respuesta aceptada 1 . Su sintaxis es bonita, pero tiene algunos defectos.
Digamos que tenemos la siguiente condición.
if $var; then
echo 'Muahahaha!'
fi
En los siguientes casos 2 , esta condición se evaluará como verdadera y ejecutará el comando anidado.
# Variable var not defined beforehand. Case 1
var='' # Equivalent to var="". # Case 2
var= # Case 3
unset var # Case 4
var='<some valid command>' # Case 5
Normalmente, solo desea que su condición se evalúe como verdadera cuando su variable "booleana", var
en este ejemplo, esté explícitamente establecida como verdadera. ¡Todos los demás casos son peligrosamente engañosos!
El último caso (#5) es especialmente complicado porque ejecutará el comando contenido en la variable (razón por la cual la condición se evalúa como verdadera para los comandos válidos 3, 4 ).
Aquí hay un ejemplo inofensivo:
var='echo this text will be displayed when the condition is evaluated'
if $var; then
echo 'Muahahaha!'
fi
# Outputs:
# this text will be displayed when the condition is evaluated
# Muahahaha!
Citar sus variables es más seguro, por ejemplo if "$var"; then
. En los casos anteriores, debería recibir una advertencia de que no se encuentra el comando. Pero aún podemos hacerlo mejor (consulte mis recomendaciones al final).
Vea también la explicación de Mike Holt sobre la respuesta original de Miku.
Problemas con la respuesta de Hbar
Este enfoque también tiene un comportamiento inesperado.
var=false
if [ $var ]; then
echo "This won't print, var is false!"
fi
# Outputs:
# This won't print, var is false!
Es de esperar que la condición anterior se evalúe como falsa y, por lo tanto, nunca se ejecute la declaración anidada. ¡Sorpresa!
Citar el valor ( "false"
), citar la variable ( "$var"
) o usar test
o [[
en lugar de [
, no hace ninguna diferencia.
Lo que recomiendo :
A continuación le recomiendo que compruebe sus "booleanos". Funcionan como se esperaba.
my_bool=true
if [ "$my_bool" = true ]; then
if [ "$my_bool" = "true" ]; then
if [[ "$my_bool" = true ]]; then
if [[ "$my_bool" = "true" ]]; then
if [[ "$my_bool" == true ]]; then
if [[ "$my_bool" == "true" ]]; then
if test "$my_bool" = true; then
if test "$my_bool" = "true"; then
Todos son prácticamente equivalentes. Tendrás que escribir algunas pulsaciones de teclas más que los métodos de las otras respuestas 5 , pero tu código será más defensivo.
Notas a pie de página
- Desde entonces, la respuesta de Miku ha sido editada y ya no contiene fallas (conocidas).
- No es una lista exhaustiva.
- Un comando válido en este contexto significa un comando que existe. No importa si el comando se usa correctamente o incorrectamente. Por ejemplo,
man woman
todavía se consideraría un comando válido, incluso si no existe dicha página de manual. - Para comandos no válidos (inexistentes), Bash simplemente se quejará de que no se encontró el comando.
- Si te importa el largo, la primera recomendación es el más corto.
Parece haber algún malentendido aquí sobre el Bash incorporado true
y, más específicamente, sobre cómo Bash expande e interpreta las expresiones entre corchetes.
El código en la respuesta de miku no tiene absolutamente nada que ver con el Bash incorporado true
, ni /bin/true
con ningún otro tipo del true
comando. En este caso, true
no es más que una simple cadena de caracteres, y nunca se realiza ninguna llamada al true
comando/integrado, ni mediante la asignación de variables ni mediante la evaluación de la expresión condicional.
El siguiente código es funcionalmente idéntico al código de la respuesta de miku:
the_world_is_flat=yeah
if [ "$the_world_is_flat" = yeah ]; then
echo 'Be careful not to fall off!'
fi
La única diferencia aquí es que los cuatro caracteres que se comparan son 'y', 'e', 'a' y 'h' en lugar de 't', 'r', 'u' y 'e'. Eso es todo. No se intenta llamar a un comando o función incorporada llamada yeah
, ni hay (en el ejemplo de miku) ningún tipo de manejo especial cuando Bash analiza el token true
. Es solo una cadena, y además completamente arbitraria.
Actualización (19 de febrero de 2014): Después de seguir el enlace en la respuesta de miku, ahora veo de dónde viene parte de la confusión. La respuesta de Miku usa corchetes simples, pero el fragmento de código al que vincula no usa corchetes. Es solo:
the_world_is_flat=true
if $the_world_is_flat; then
echo 'Be careful not to fall off!'
fi
Ambos fragmentos de código se comportarán de la misma manera, pero los corchetes cambian por completo lo que sucede bajo el capó.
Esto es lo que Bash está haciendo en cada caso:
Sin paréntesis:
- Expanda la variable
$the_world_is_flat
a la cadena"true"
. - Intente analizar la cadena
"true"
como un comando. - Busque y ejecute el
true
comando (ya sea integrado o/bin/true
, según la versión de Bash). - Compare el código de salida del
true
comando (que siempre es 0) con 0. Recuerde que en la mayoría de los shells, un código de salida de 0 indica éxito y cualquier otra cosa indica fracaso. - Dado que el código de salida era 0 (éxito), ejecute la cláusula
if
de la declaraciónthen
Soportes:
- Expanda la variable
$the_world_is_flat
a la cadena"true"
. - Analice la expresión condicional ahora completamente expandida, que tiene la forma
string1 = string2
. El operador es el operador de comparación de cadenas=
de bash . Entonces... - Haga una comparación de cadenas en
"true"
y"true"
. - Sí, las dos cadenas eran iguales, por lo que el valor del condicional es verdadero.
- Ejecute la cláusula
if
de la declaraciónthen
.
El código sin corchetes funciona porque el true
comando devuelve un código de salida de 0, lo que indica éxito. El código entre corchetes funciona porque el valor de $the_world_is_flat
es idéntico al literal de cadena true
en el lado derecho del archivo =
.
Sólo para aclarar el punto, considere los siguientes dos fragmentos de código:
Este código (si se ejecuta con privilegios de root) reiniciará su computadora:
var=reboot
if $var; then
echo 'Muahahaha! You are going down!'
fi
Este código simplemente imprime "Buen intento". No se llama al comando de reinicio.
var=reboot
if [ $var ]; then
echo 'Nice try.'
fi
Actualización (2014-04-14) Para responder la pregunta en los comentarios sobre la diferencia entre =
y ==
: AFAIK, no hay diferencia. El ==
operador es un sinónimo específico de Bash =
y, hasta donde he visto, funcionan exactamente igual en todos los contextos.
Sin embargo, tenga en cuenta que me refiero específicamente a los operadores de comparación de cadenas =
y ==
utilizados en las pruebas [ ]
or [[ ]]
. No estoy sugiriendo eso =
y ==
son intercambiables en todas partes en bash.
Por ejemplo, obviamente no puedes hacer una asignación de variables con ==
, como var=="foo"
(bueno, técnicamente puedes hacer esto, pero el valor de var
será "=foo"
, porque Bash no ve un ==
operador aquí, está viendo un =
operador (asignación), seguido de el valor literal ="foo"
, que simplemente se convierte en "=foo"
).
Además, aunque =
y ==
son intercambiables, debes tener en cuenta que el funcionamiento de esas pruebas depende de si las estás usando dentro [ ]
de o [[ ]]
y también de si los operandos están entrecomillados o no. Puede leer más sobre esto en Guía avanzada de secuencias de comandos Bash: 7.3 Otros operadores de comparación (desplácese hacia abajo hasta la discusión sobre =
y ==
).
Larga historia corta:
No hay booleanos en Bash
los comandos true
yfalse
Bash tiene expresiones booleanas en términos de comparación y condiciones. Dicho esto, lo que puedes declarar y comparar en Bash son cadenas y números. Eso es todo.
Dondequiera que vea true
o false
en Bash, es una cadena o un comando/integrado que solo se usa para su código de salida.
Esta sintaxis...
if true; then ...
Es esencial...
if COMMAND; then ...
donde esta el comando true
. La condición es verdadera siempre que el comando devuelve el código de salida 0. Son funciones integradas de Bash true
y false
, a veces, también programas independientes que no hacen más que devolver el código de salida correspondiente.
Condiciones enif..then..fi
Cuando utiliza corchetes o el test
comando, confía en el código de salida de esa construcción. Tenga en cuenta que [ ]
y [[ ]]
también son solo comandos/integrados como cualquier otro. Entonces ...
if [[ 1 == 1 ]]; then echo yes; fi
corresponde a
if COMMAND; then echo yes; fi
y COMMAND
aquí está [[
con los parámetros1 == 1 ]]
La if..then..fi
construcción es simplemente azúcar sintáctica. Siempre puedes simplemente ejecutar los comandos separados por un doble símbolo para obtener el mismo efecto:
[[ 1 == 1 ]] && echo yes
Al usar true
y false
en estas construcciones de prueba, en realidad solo está pasando la cadena "true"
o "false"
al comando de prueba. Aquí hay un ejemplo:
Lo creas o no, pero todas esas condiciones producen el mismo resultado :
if [[ false ]]; then ...
if [[ "false" ]]; then ...
if [[ true ]]; then ...
if [[ "true" ]]; then ...
TL;DR; comparar siempre con cadenas o números
Para dejar esto claro a los futuros lectores, recomendaría utilizar siempre comillas alrededor true
y false
:
HACER
if [[ "${var}" == "true" ]]; then ...
if [[ "${var}" == "false" ]]; then ...
if [[ "${var}" == "yes" ]]; then ...
if [[ "${var}" == "USE_FEATURE_X" ]]; then ...
if [[ -n "${var:-}" ]]; then echo "var is not empty" ...
NO
# Always use double square brackets in bash!
if [ ... ]; then ...
# This is not as clear or searchable as -n
if [[ "${var}" ]]; then ...
# Creates impression of Booleans
if [[ "${var}" != true ]]; then ...
# `-eq` is for numbers and doesn't read as easy as `==`
if [[ "${var}" -eq "true" ]]; then ...
Tal vez
# Creates impression of Booleans.
# It can be used for strict checking of dangerous operations.
# This condition is false for anything but the literal string "true".
if [[ "${var}" != "true" ]]; then ...
Utiliza expresiones aritméticas.
#!/bin/bash
false=0
true=1
((false)) && echo false
((true)) && echo true
((!false)) && echo not false
((!true)) && echo not true
Producción:
verdadero
no falso