Pregunta ¿Cómo declarar y usar variables booleanas en script de shell?


Traté de 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 la siguiente sintaxis para usar variables booleanas como expresiones correctas:

if [ $variable ]

if [ !$variable ]

649
2018-06-01 21:54


origen


Respuestas:


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 cual la respuesta original está incluida aquí es porque los comentarios antes de la revisión del 12 de febrero de 2014 se refieren solo 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 builtin true el 2 de junio de 2010 solo se aplica a la respuesta original, no a la revisada.


815
2018-06-01 21:58



TL; DR

bool=true

if [ "$bool" = true ]

Problemas con Miku (original) responder

hago no recomendar la respuesta aceptada1. Su sintaxis es bonita pero tiene algunos defectos.

Digamos que tenemos la siguiente condición.

if $var; then
  echo 'Muahahaha!'
fi

En los siguientes casos2, esta condición evaluará a cierto y ejecuta 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, se establece explícitamente en verdadero. ¡Todos los demás casos son peligrosamente engañosos!

El último caso (n. ° 5) es especialmente travieso porque ejecutará el comando contenido en la variable (por lo que la condición se evalúa como verdadera para 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 tus variables es más seguro, p. if "$var"; then. En los casos anteriores, debería recibir una advertencia de que no se encontró el comando. Pero aún podemos hacerlo mejor (ver mis recomendaciones en la parte inferior).

También vea 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, por lo tanto, nunca se ejecutará la declaración anidada. ¡Sorpresa!

Citando el valor ("false"), citando la variable ("$var"), o usando test o [[ en lugar de [, no hagas la diferencia

Lo que sí recomiendo:

Aquí hay maneras en que te recomiendo que revises tus "booleanos". Funcionan como se esperaba

bool=true

if [ "$bool" = true ]; then
if [ "$bool" = "true" ]; then

if [[ "$bool" = true ]]; then
if [[ "$bool" = "true" ]]; then
if [[ "$bool" == true ]]; then
if [[ "$bool" == "true" ]]; then

if test "$bool" = true; then
if test "$bool" = "true"; then

Todos son bastante equivalentes. Tendrá que escribir algunas teclas más que los enfoques en las otras respuestas5 pero tu código será más defensivo.


Notas a pie de página

  1. La respuesta de Miku ha sido editada desde entonces y ya no contiene fallas (conocidas).
  2. No es una lista exhaustiva.
  3. Un comando válido en este contexto significa un comando que existe. No importa si el comando se usa de forma correcta o incorrecta. P.ej. man woman aún se consideraría un comando válido, incluso si no existe dicha página de manual.
  4. Para comandos no válidos (inexistentes), Bash simplemente se quejará de que no se encontró el comando.
  5. Si te preocupa la longitud, la primera recomendación es la más corta.

595
2018-01-18 22:57



Parece que hay un malentendido aquí sobre el bash builtin true, y más específicamente, acerca de 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, ni ningún otro sabor del true mando. En este caso, true no es más que una cadena de caracteres simple, y no hay llamada al true comando / builtin se hace alguna vez, ni por la asignación de variable, ni por la evaluación de la expresión condicional.

El siguiente código es funcionalmente idéntico al código en la respuesta de miku:

the_world_is_flat=yeah
if [ "$the_world_is_flat" = yeah ]; then
    echo 'Be careful not to fall off!'
fi

los solamente La 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. No se intenta llamar a un comando o builtin llamado 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 completamente arbitraria.

Actualización (19/02/2014): Después de seguir el enlace en la respuesta de miku, ahora veo de dónde proviene la confusión. La respuesta de Miku usa corchetes individuales, pero el fragmento de código al que se 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 serán comportarse de la misma manera, pero los corchetes cambian completamente lo que sucede debajo del capó.

Esto es lo que bash está haciendo en cada caso:

Sin corchetes

  1. Expande la variable $the_world_is_flat a la cuerda "true".
  2. Intentar analizar la cadena "true" como un comando.
  3. Encuentra y ejecuta el true comando (ya sea incorporado o /bin/true, dependiendo de la versión de bash).
  4. Compare el código de salida de la true comando (que siempre es 0) con 0. Recuerde que en la mayoría de las shells, un código de salida de 0 indica éxito y cualquier otra cosa indica falla.
  5. Como el código de salida fue 0 (correcto), ejecute el if afirmación then cláusula

Soportes:

  1. Expande la variable $the_world_is_flat a la cuerda "true".
  2. Analice la expresión condicional ahora completamente expandida, que tiene la forma string1 = string2. los = el operador es bash comparación de cadenas operador. Asi que...
  3. Haga una comparación de cadenas en "true" y "true".
  4. Sí, las dos cadenas son las mismas, por lo que el valor del condicional es verdadero.
  5. Ejecuta el if afirmación then cláusula.

El código de no corchetes funciona porque el true comando devuelve un código de salida de 0, que indica éxito. El código entre corchetes funciona porque el valor de $the_world_is_flat es idéntico al literal de la cadena true en el lado derecho de la =.

Para aclarar el punto, considere los siguientes dos fragmentos de código:

Este código (si se ejecuta con privilegios de raíz) reiniciará su computadora:

var=reboot
if $var; then
  echo 'Muahahaha! You are going down!'
fi

Este código simplemente imprime "Buen intento". El comando de reinicio no es llamado.

var=reboot
if [ $var ]; then
  echo 'Nice try.'
fi

Actualización (4/14/2014) Para responder a la pregunta en los comentarios sobre la diferencia entre = y ==: AFAIK, no hay diferencia. los == operador es un sinónimo específico de bash para =y, por lo que he visto, funcionan exactamente igual en todos los contextos. Tenga en cuenta, sin embargo, que estoy hablando específicamente de la = y == operadores de comparación de cadenas utilizados en cualquiera [ ] o [[ ]] pruebas. No estoy sugiriendo eso = y == son intercambiables en todos lados en bash. Por ejemplo, obviamente no puedes hacer la asignación de variables con ==, como var=="foo" (bien técnicamente poder haz esto, pero el valor de var estarán "=foo"porque bash no está viendo == operador aquí, está viendo una = (asignación) operador, seguido del valor literal ="foo", que simplemente se convierte "=foo")

Además, aunque = y == son intercambiables, debe tener en cuenta que esas pruebas funcionan hace Depende de si lo estás usando dentro [ ] o [[ ]], y también si los operandos están cotizados o no. Puedes leer mas al respecto aquí: Guía avanzada de scripts de Bash: 7.3 Otros operadores de comparación (desplácese hacia abajo para la discusión de = y ==)


99
2018-02-13 23:45



Usa expresiones aritméticas.

#!/bin/bash

false=0
true=1

((false)) && echo false
((true)) && echo true
((!false)) && echo not false
((!true)) && echo not true

Salida:

cierto
    No falso


40
2017-11-13 23:44



Hace mucho tiempo, cuando todo lo que teníamos era sh, booleanos donde se manejan confiando en una convención de test programa donde test devuelve un estado de salida falso si se ejecuta sin argumentos. Esto le permite a uno pensar en una variable que está desarmada como falsa y la variable se establece en cualquier valor como verdadero. Hoy, la prueba está incorporada bash y es comúnmente conocido por su alias de un personaje [ (o un ejecutable para usar en shells que carecen de él, como notas dolmen):

FLAG="up or <set>"

if [ "$FLAG" ] ; then 
    echo 'Is true'
else 
    echo 'Is false'
fi

# unset FLAG
#    also works
FLAG=

if [ "$FLAG" ] ; then
    echo 'Continues true'
else
    echo 'Turned false'
fi

Debido a las convenciones de citas, los guionistas prefieren usar el comando compuesto [[ que imita test pero tiene una sintaxis más agradable: las variables con espacios no necesitan ser citadas, se puede usar && y || como operadores lógicos con precedencia extraña, y no hay limitaciones POSIX en el número de términos.

Por ejemplo, para determinar si FLAG está configurado y COUNT es un número mayor que 1:

FLAG="u p"
COUNT=3

if [[ $FLAG  && $COUNT -gt '1' ]] ; then 
    echo 'Flag up, count bigger than 1'
else 
    echo 'Nope'
fi

Esto puede confundir cuando se necesitan espacios, cadenas de longitud cero y variables nulas, y también cuando el script necesita trabajar con varios shells.


14
2018-04-10 20:23



¿Cómo declarar y usar variables booleanas en script de shell?

A diferencia de muchos otros lenguajes de programación, Bash no segrega sus variables por "tipo". [1]

Entonces la respuesta es bastante clara. No hay boolean variable en bash. Sin embargo :

Usando una declaración de declaración, podemos limitar la asignación de valores a las variables.[2]

#!/bin/bash
declare -ir BOOL=(0 1) #remember BOOL can't be unset till this shell terminate
readonly false=${BOOL[0]}
readonly true=${BOOL[1]}
#same as declare -ir false=0 true=1
((true)) && echo "True"
((false)) && echo "False"
((!true)) && echo "Not True"
((!false)) && echo "Not false"

los r opción en declare y readonly se usa para indicar explícitamente que las variables son solo lectura. Espero que el propósito sea claro.


11
2018-04-30 06:19



En lugar de fingir un booleano y dejar una trampa para futuros lectores, ¿por qué no simplemente utilizar un valor mejor que verdadero y falso?

Por ejemplo:

build_state=success
if something-horrible; then
  build_state=failed
fi

if [[ "$build_state" == success ]]; then
  echo go home, you are done
else
  echo your head is on fire, run around in circles
fi

6
2017-09-08 16:55



Larga historia corta:

No hay booleanos en bash

Lo que bash tiene, son expresiones booleanas en términos de comparación y condiciones. Dicho eso, lo que puedes declarar y comparar en bash son cadenas y números. Eso es.

Donde sea que veas true o false en bash, es un string o un comando / builtin que solo se usa para su código de salida.

Esta sintaxis ...

if true; then ...

Es esencial...

if COMMAND; then ...

La condición es verdadera siempre que el comando devuelva el código de salida 0.

Podrías hacer esto:

if which foo; then echo "program foo found"; fi

Al usar corchetes o el test comando, confía en el código de salida de esa construcción. Manten eso en mente [ ] y [[ ]] también son solo comandos / comandos integrados como cualquier otro. Asi que ...

if [[ 1 == 1 ]]; then echo yes; fi

es solo azúcar sintáctico para ...

[[ 1 == 1 ]] && echo yes

Entonces al usar true y false en cualquiera de los constructos antes mencionados, en realidad solo está pasando la cadena "true" o "false" al comando de prueba. Aquí un ejemplo:

Créalo o no, pero esas condiciones son todas ceder el mismo resultado:

if [[ false ]]; then ...
if [[ "false" ]]; then ...
if [[ true ]]; then ...
if [[ "true" ]]; then ...

TL; DR; siempre compare contra cadenas o números

Para dejar esto en claro a los lectores futuros, recomendaría usar siempre comillas true y false:

HACER

if [[ "${var}" == "true" ]]; then ...
if [[ "${var}" == "false" ]]; then ...
if [[ -n "${var:-}" ]]; then echo "var is not empty" ...

NO

if [ ... ]; then ...  # always use double square brackets in bash!
if [[ "${var}" ]]; then ...  # this is not as clear or searchable as -n
if [[ "${var}" != true ]]; then ...  # creates impression of booleans
if [[ "${var}" -eq "true" ]]; then ...  # `-eq` is for numbers and doesn't read as easy as `==`

Tal vez

if [[ "${var}" != "true" ]]; then ...  # creates impression of booleans. Can be used for strict checking of dangerous operations. This condition is false for anything but the literal string "true". 

6
2017-11-03 09:54



POSIX (Interfaz de sistema operativo portátil)

Extraño aquí el punto clave, que es la portabilidad. Es por eso que mi encabezado tiene POSIX en si mismo.

Básicamente, todas las respuestas votadas son correctas, con la excepción de que son INTENTO-específico demasiado.

Entonces, básicamente, solo deseo agregar más información sobre la portabilidad.


  1. [ y ] corchetes como en [ "$var" = true ] no son necesarios, y puede omitirlos y usar el test comando directamente:

    test "$var" = true && CodeIfTrue || CodeIfFalse
    
  2. Imagina lo que esas palabras true y false significa para el caparazón, pruébelo usted mismo:

    echo $((true))
    
    0
    
    echo $((false))
    
    1
    

    Pero usando comillas:

    echo $(("true"))
    
    bash: "true": syntax error: operand expected (error token is ""true"")
    sh (dash): sh: 1: arithmetic expression: expecting primary: ""true""
    

    Lo mismo va para:

    echo $(("false"))
    

    El shell no puede interpretarlo más que una cadena. Espero que te estés dando cuenta de qué tan bueno es usar palabras clave adecuadas sin citas.

    Pero nadie lo dijo en respuestas anteriores.

  3. ¿Lo que esto significa? Bueno, varias cosas.

    • Deberías acostumbrarte a que las palabras clave booleanas en realidad sean tratadas como números, eso es true = 0 y false = 1, recuerda que todos los valores distintos de cero se tratan como false.

    • Dado que se tratan como números, debe tratarlos así también, es decir, si define una variable, diga:

      var_a=true
      echo "$var_a"
      
       true
      

      puedes crear un valor opuesto al mismo con:

      var_a=$((1 - $var_a))
      echo "$var_a"
      
      1
      

      Como puede ver, el caparazón imprime true cadena por primera vez que lo usa, pero desde entonces, todo funciona a través de un número 0 o 1, respectivamente.


Finalmente, qué debes hacer con toda esa información

  • El primer buen hábito sería asignar 0 en lugar de true; 1 en lugar de false.

  • El segundo buen hábito sería probar si la variable es / no es igual a cero:

    test "$var" -eq 0 && CodeIfTrue || CodeIfFalse
    

2
2018-02-01 17:45



Bill Parker está siendo rechazado porque sus definiciones están revertidas de la convención de código normal. Normalmente, true se define como 0 y false se define como distinto de cero. 1 funcionará para falso, al igual que 9999 y -1. Lo mismo con los valores de retorno de la función: 0 es correcto y todo lo que no sea cero es un error. Lo siento, todavía no tengo las credenciales de la calle para votar o para responderle directamente.

Bash recomienda usar corchetes dobles ahora como hábito en lugar de corchetes individuales, y el enlace que brindó Mike Holt explica las diferencias en cómo funcionan. 7.3. Otros operadores de comparación

Por un lado, eq es un operador numérico, por lo que tiene el código

#**** NOTE *** This gives error message *****
The_world_is_flat=0;
if [ "${The_world_is_flat}" -eq true ]; then

emitirá una declaración de error, esperando una expresión entera. Esto se aplica a cualquiera de los parámetros, ya que ninguno es un valor entero. Sin embargo, si ponemos corchetes dobles alrededor, no emitirá una declaración de error, pero arrojará un valor incorrecto (bueno, en el 50% de las posibles permutaciones). Evaluará a [[0 -eq verdadero]] = éxito pero también a [[0 -eq falso]] = éxito, lo cual es incorrecto (hmmm ... ¿qué ocurre si ese valor es un valor numérico?).

#**** NOTE *** This gives wrong output *****
The_world_is_flat=true;
if [[ "${The_world_is_flat}" -eq true ]]; then

Hay otras permutaciones del condicional que también darán resultados erróneos. Básicamente, cualquier cosa (que no sea la condición de error enumerada anteriormente) que establece una variable en un valor numérico y la compara con un builtin verdadero / falso, o establece una variable en un builtin verdadero / falso y lo compara con un valor numérico. Además, cualquier cosa que establezca una variable en un builtin verdadero / falso y haga una comparación usando -eq. Así que evite -eq para las comparaciones booleanas y evite el uso de valores numéricos para las comparaciones booleanas. Aquí hay un resumen de las permutaciones que darán resultados no válidos:

#With variable set as an integer and evaluating to true/false
#*** This will issue error warning and not run: *****
The_world_is_flat=0;
if [ "${The_world_is_flat}" -eq true ]; then

#With variable set as an integer and evaluating to true/false
#*** These statements will not evaluate properly: *****
The_world_is_flat=0;
if [ "${The_world_is_flat}" -eq true ]; then
#
if [[ "${The_world_is_flat}" -eq true ]]; then
#
if [ "${The_world_is_flat}" = true ]; then
#
if [[ "${The_world_is_flat}" = true ]]; then
#
if [ "${The_world_is_flat}" == true ]; then
#
if [[ "${The_world_is_flat}" == true ]]; then


#With variable set as an true/false builtin and evaluating to true/false
#*** These statements will not evaluate properly: *****
The_world_is_flat=true;
if [[ "${The_world_is_flat}" -eq true ]]; then
#
if [ "${The_world_is_flat}" = 0 ]; then
#
if [[ "${The_world_is_flat}" = 0 ]]; then
#
if [ "${The_world_is_flat}" == 0 ]; then
#
if [[ "${The_world_is_flat}" == 0 ]]; then

Entonces, ahora a lo que funciona. Use las instrucciones internas verdaderas / falsas tanto para su comparación como para sus evaluaciones (como señaló Mike Hunt, no las incluya entre comillas). Luego use uno o dos signos iguales (= o ==) y corchetes simples o dobles ([] o [[]]). Personalmente, me gusta el signo de doble igual porque me recuerda comparaciones lógicas en otros lenguajes de programación, y comillas dobles solo porque me gusta escribir. Entonces estos funcionan:

#With variable set as an integer and evaluating to true/false
#*** These statements will work properly: *****
#
The_world_is_flat=true/false;
if [ "${The_world_is_flat}" = true ]; then
#
if [[ "${The_world_is_flat}" = true ]]; then
#
if [ "${The_world_is_flat}" = true ]; then
#
if [[ "${The_world_is_flat}" == true ]]; then

Ahí tienes.


1
2017-09-21 00:38



Aquí hay una implementación de una mano corta if true.

# Function to test if a variable is set to "true"
_if () {
    [ "${1}" == "true" ] && return 0
    [ "${1}" == "True" ] && return 0
    [ "${1}" == "Yes" ] && return 0
    return 1
}

Ejemplo 1

my_boolean=true

_if ${my_boolean} && {
    echo "True Is True"
} || {
    echo "False Is False"
}

Ejemplo 2 

my_boolean=false
! _if ${my_boolean} && echo "Not True is True"

1
2018-03-09 13:49