¿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 syntax:

variable=$false variable=$true 

¿Es esto correcto? Además, si quisiera actualizar esa variable, ¿usaría la misma syntax? Finalmente, es la siguiente syntax para usar variables booleanas como expresiones correctas:

 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 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.

TL; DR

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

Problemas con la respuesta original de Miku

No recomiendo la respuesta aceptada 1 . Su syntax 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 nested.

 # Variable var not defined beforehand. Case 1 var='' # Equivalent to var="". Case 2 var= # Case 3 unset var # Case 4 var='' # Case 5 

Normalmente, solo desea que su condición se evalúe como verdadera cuando su variable “booleana”, var en este ejemplo, se configure explícitamente como verdadera. ¡Todos los demás casos son peligrosamente engañosos!

El último caso (n. ° 5) es especialmente malo porque ejecutará el comando contenido en la variable (por lo que 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 tus variables es más seguro, por ejemplo, if "$var"; then 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 statement anidada. ¡Sorpresa!

Citar el valor ( "false" ), citar la variable ( "$var" ), o usar test o [[ lugar de [ , no hacen 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 respuestas 5, pero su 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. Por ejemplo man woman todavía se consideraría una orden válida, 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.

Parece que hay algunos malentendidos acerca de 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 con ningún otro sabor del true comando. En este caso, true no es más que una cadena de caracteres simple, y no se realiza ninguna llamada al true comando / builtin, 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 

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. No hay ningún bash de invocar un comando o un builtin llamado yeah , ni tampoco existe (en el ejemplo de miku) algún tipo de manejo especial cuando Bash analiza el token como true . Es solo una cadena, y completamente arbitraria.

Actualización (2014-02-19): 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 se comportarán de la misma manera, pero los corchetes cambian por completo lo que sucede debajo del capó.

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

Sin corchetes

  1. Expanda la variable $the_world_is_flat a la cadena "true" .
  2. Intente analizar la cadena "true" como un comando.
  3. Encuentre y ejecute el comando true (ya sea un built-in o /bin/true , dependiendo de la versión de Bash).
  4. Compare el código de salida del 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. Dado que el código de salida fue 0 (éxito), ejecute la cláusula then if statement

Soportes:

  1. Expanda la variable $the_world_is_flat a la cadena "true" .
  2. Analice la expresión condicional ahora completamente expandida, que es de la forma string1 = string2 . El operador = es el operador de comparación de cadenas de bash. 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 la cláusula then if statement.

El código de no corchetes funciona, porque el comando true 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 string literal true en el lado derecho de the = .

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

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

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

Este código simplemente imprime “Buen bash”. El comando de reinicio no es llamado.

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

Actualización (14/04/2014) Para responder a la pregunta en los comentarios sobre la diferencia entre = y == : AFAIK, no hay diferencia. El operador == es un sinónimo específico de Bash para = , y hasta donde he visto, funcionan exactamente igual en todos los contextos.

Sin embargo, tenga en cuenta que estoy hablando específicamente de los operadores de comparación de cadenas = y == utilizados en las pruebas [ ] o [[ ]] . No estoy sugiriendo que = y == sean intercambiables en todas partes en bash.

Por ejemplo, obviamente no puedes hacer asignaciones de variables con == , como var=="foo" (bien técnicamente puedes hacer esto, pero el valor de var será "=foo" , porque Bash no está viendo un == operador aquí, está viendo un operador = (asignación), seguido por el valor literal ="foo" , que simplemente se convierte en "=foo" ).

Además, aunque = y == son intercambiables, debe tener en cuenta que el funcionamiento de esas pruebas depende de si lo está utilizando dentro de [ ] o [[ ]] , y también si los operandos están o no citados. Puede leer más sobre esto en Advanced Bash Scripting Guide: 7.3 Otros operadores de comparación (baje hasta la discusión de = y == ).

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

Hace mucho tiempo, cuando todo lo que teníamos era sh , los booleanos se manejaban confiando en una convención del progtwig de test donde la 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á integrada en bash y es comúnmente conocida por su carácter alias [ (o un ejecutable para usar en shells que carecen de él, como dolmen notes):

 FLAG="up or " 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 la test pero tiene una syntax 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="up" COUNT=3 if [[ $FLAG && $COUNT -gt '1' ]] ; then echo 'Flag up, count bigger than 1' else echo 'Nope' fi 

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

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

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

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

Usando una statement de statement, 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" 

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

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 una cadena o un comando / builtin que solo se usa para su código de salida.

Esta syntax …

 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 

Cuando usa corchetes o el comando de test , confía en el código de salida de ese constructo. Tenga en cuenta que [ ] 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 

Por lo tanto, al usar true y false en cualquiera de las construcciones antes mencionadas, en realidad solo está pasando la cadena "true" o "false" al comando de prueba. Aquí un ejemplo:

Créalo o no, pero esas condiciones están dando 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 que esto quede claro para los lectores en el futuro, recomendaría utilizar siempre citas entre 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". 

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 

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 sí mismo.

Esencialmente, todas las respuestas votadas son correctas, con la excepción de que son BASH -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 comando de test directamente:

     test "$var" = true && CodeIfTrue || CodeIfFalse 
  2. Imagina qué significan esas palabras true y false para el caparazón, pruébalo tú 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 tenga la idea de qué tan bueno es usar palabras clave adecuadas sin comillas.

    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 se tratan como números, es decir 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 por sí mismo, el shell imprime true cuerda true por primera vez, pero desde entonces funciona con el número 0 o 1 , respectivamente.


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

  • El primer buen hábito sería asignar 0 lugar de true ; 1 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 

Bill Parker está siendo rechazado , porque sus definiciones están revertidas de la convención del 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, aún no tengo la credibilidad 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 statement 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 statement 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 -eq 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 progtwigció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.

Aquí hay una implementación de short handed 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" 

Aquí hay un ejemplo simple que funciona para mí:

 temp1=true temp2=false if [ "$temp1" = true ] || [ "$temp2" = true ] then echo "Do something." else echo "Do something else." fi 

Encontré las respuestas existentes confusas.

Personalmente, solo quiero tener algo que se vea y funcione como C.

Este fragmento funciona muchas veces al día en producción:

 snapshotEvents=true if ($snapshotEvents) then # do stuff if true fi 

y para mantener a todos contentos, probé:

 snapshotEvents=false if !($snapshotEvents) then # do stuff if false fi 

Lo cual también funcionó bien.

$snapshotEvents evalúa el contenido del valor de la variable. Entonces necesitas los $ .

Realmente no necesitas los paréntesis, solo los encuentro útiles.

  • Probado en: GNU Bash, versión 4.1.11 (2) -release

  • Guía Bash para principiantes , Machtelt Garrels, v1.11, 2008

Aquí hay una mejora en la respuesta original de miku, que aborda las preocupaciones de Dennis Williamson sobre el caso, donde la variable no está establecida:

 the_world_is_flat=true if ${the_world_is_flat:-false} ; then echo "Be careful not to fall off!" fi 

Y para probar si la variable es false :

 if ! ${the_world_is_flat:-false} ; then echo "Be careful not to fall off!" fi 

Acerca de otros casos con un contenido desagradable en la variable, este es un problema con cualquier entrada externa alimentada a un progtwig.

Cualquier entrada externa debe ser validada antes de confiar en ella. Pero esa validación tiene que hacerse solo una vez, cuando se recibe esa entrada.

No tiene que afectar el rendimiento del progtwig al hacerlo en cada uso de la variable, como sugiere Dennis Williamson .

Usa el sentido común. En muchos idiomas, 1 es verdadero y 0 es falso. Afortunadamente esto también va con Bash.

Entrada:

 val=1 ((val)) && echo "true" || echo "false" val=0 ((val)) && echo "true" || echo "false" 

Salida:

 true false 

Fuente :

((expresión))

La expresión se evalúa de acuerdo con las reglas descritas a continuación bajo EVALUACIÓN ARITMÉTICA. Si el valor de la expresión no es cero, el estado de retorno es 0; de lo contrario, el estado de retorno es 1. Esto es exactamente equivalente a dejar “expresión”.

Bash realmente confunde el problema con los gustos de [ , [[ , (( , $(( , etc.

Todos tratando en los espacios del código de los demás. Supongo que esto es principalmente histórico, donde Bash tuvo que fingir ser sh ocasionalmente.

La mayoría de las veces, puedo elegir un método y atenerme a él. En este caso, tiendo a declarar (preferiblemente en un archivo de biblioteca común que puedo incluir con . En mis guiones reales).

 TRUE=1; FALSE=0 

Puedo usar el operador aritmético (()) para probarlo.

 testvar=$FALSE if [[ -d ${does_directory_exist} ]] then testvar=$TRUE; fi if (( testvar == TRUE )); then # do stuff because the directory does exist fi 
  1. Tienes que ser disciplinado. Su testvar debe establecerse en $TRUE o $FALSE en todo momento.

  2. En (()) comparadores, no necesita los $ anteriores, lo que lo hace más legible.

  3. Puedo usar (()) porque $TRUE=1 y $FALSE=0 , es decir, valores numéricos.

  4. La desventaja es tener que usar $ ocasionalmente:

     testvar=$TRUE 

    que no es tan bonito

No es una solución perfecta, pero cubre todos los casos, necesito una prueba de este tipo.