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

981

Traté de declarar una variable booleana en un script de shell utilizando 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 ]
hassaanm
fuente
73
¡TENER CUIDADO! truey falseen el contexto de la mayoría de los fragmentos a continuación, son solo cadenas simples, no el bash built-ins!!! Lea la respuesta de Mike Holt a continuación. (Este es un ejemplo en el que una respuesta muy votada y aceptada es confusa en mi humilde opinión y oculta el contenido perspicaz en respuestas menos votadas)
mjv
77
@mjv La mayor parte de la confusión sobre esta pregunta (y la respuesta de Miku) se debió al hecho de que Miku revisó su respuesta en algún momento después de que se publicaron varios comentarios que describían cómo la respuesta de Miku implicaba llamar al bash incorporado true. Resulta que la respuesta original de Miku realmente llamó al trueincorporado, pero la respuesta revisada no. Esto provocó que dichos comentarios parecieran estar equivocados sobre cómo funcionaba el código de Miku. La respuesta de Miku ha sido editada para mostrar explícitamente tanto el código original como el revisado. Esperemos que esto alivie la confusión de una vez por todas.
Mike Holt
2
[ true ] && echo yes, true is truey (upppsss) [ false ] && echo yes, false is also true. / bin / true y / bin / false da un código de retorno $? para funciones no para comparación.
fcm
Si var se establece variable=somethingde lo que es cierto, si no se establece, variable=eso sería falso [[ $variable ]] && echo true || echo falsee inverso[[ ! $variable ]] && echo false || echo true
Ivan

Respuestas:

1203

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: Uso de variables booleanas en Bash

La razón por la cual la respuesta original se incluye 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 trueel 2 de junio de 2010 solo se aplica a la respuesta original, no a la revisada.

miku
fuente
37
Para explicar lo que está sucediendo: la ifinstrucción está ejecutando el contenido de la variable que es el Bash incorporado true. Cualquier comando podría establecerse como el valor de la variable y se evaluaría su valor de salida.
Pausado hasta nuevo aviso.
77
@pms Los operadores "-o" y "-a" son solo para el comando "prueba" (también conocido como "[]"). En cambio, esto es "si + comando", sin la "prueba". (Como "if grep foo file; then ...".) Entonces, use los operadores normal &&y ||: # t1=true; t2=true; f1=false;# if $t1 || $f1; then echo is_true ; else echo is_false; fi; (devuelve "true", ya que t1 = true) # if $t1 && $f1 || $t2; then echo is_true ; else echo is_false; fi (devuelve "true", ya que t2 = true) . Nuevamente, esto SOLO funciona porque "verdadero" / "falso" son bash-builtins (devolviendo verdadero / falso). No puede usar "if $ var ..." a menos que var sea un cmd (es decir, verdadero o falso)
michael
14
-1, mira mi respuesta para una explicación.
Dennis
3
Mucha información incorrecta, aquí. / bin / true no se está utilizando de manera efectiva. Ver la respuesta de Dennis.
ajk
1
Este código no es el mismo y no funciona de la misma manera que el artículo vinculado. El código vinculado llama a un programa por el nombre almacenado en una variable, pero el código en esta respuesta es solo una comparación de cadenas.
Preguntas de Quolonel
795

TL; DR

bool=true

if [ "$bool" = true ]

Problemas con la respuesta ( original ) de Miku

Yo 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

Por lo general, solo desea que su condición se evalúe como verdadera cuando su variable "booleana", varen este ejemplo, se establece explícitamente en verdadera. ¡Todos los demás casos son peligrosamente engañosos!

El último caso (# 5) es especialmente travieso 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 (vea mis recomendaciones al final).

También vea la explicación de Mike Holt de 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 que nunca se ejecutará la instrucción anidada. ¡Sorpresa!

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

Lo que hago recomiendo:

Aquí hay formas en que le recomiendo que revise sus "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 más o menos 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 al pie

  1. La respuesta de Miku ya ha sido editada 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 correcta o incorrectamente. P.ejman woman , aún se consideraría un comando válido, incluso si no existe tal página de manual.
  4. Para los comandos no válidos (inexistentes), Bash simplemente se quejará de que no se encontró el comando.
  5. Si te importa la longitud, la primera recomendación es la más corta.
Dennis
fuente
8
Usar ==con [o testno es portátil. Teniendo en cuenta la portabilidad es la única ventaja [/ testtiene más [[, quédese con =.
chepner
2
@Scott Utilizo el pescado como mi caparazón principal, que en mi opinión tiene un lenguaje de script sensato en comparación con bash.
Dennis
1
Sí, simplemente no pude encontrar en los comentarios ninguna apreciación por este chiste oculto, así que tuve que señalarlo =)
Kranach
55
Para mí, conceptualmente es más fácil de entender si uso bool = "true". Entonces está claro que es solo una cadena y no un valor especial o incorporado.
wisbucky
1
@dolmen absolutamente, evaluar la entrada no es tan arriesgado cuando la controlas, pero aún así considero que es una mala práctica que debería evitarse si se puede evitar fácilmente. Alguien que solo haya visto y utilizado el estilo anterior puede no conocer sus defectos que pueden causar un comportamiento inesperado.
Dennis
175

Parece que hay algunos malentendidos aquí sobre el Bash incorporado true, y más específicamente, sobre cómo Bash expande e interpreta las expresiones entre paréntesis.

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 truecomando. En este caso, trueno es más que una simple cadena de caracteres, y nunca se realiza ninguna llamada al truecomando / incorporado, ni por la asignación de variables, 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 se ha intentado llamar a un comando o nombre incorporado 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 parte de la confusión. La respuesta de Miku usa corchetes simples, pero el fragmento de código al que enlaza 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 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. Expande la variable $the_world_is_flata la cadena "true".
  2. Intente analizar la cadena "true"como un comando.
  3. Busque y ejecute el truecomando (ya sea un incorporado o /bin/true, dependiendo de la versión de Bash).
  4. Compare el código de salida del truecomando (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 falla.
  5. Como el código de salida fue 0 (correcto), ejecute la cláusula de la ifdeclaraciónthen

Soportes:

  1. Expande la variable $the_world_is_flata la cadena "true".
  2. Analiza la expresión condicional ahora completamente expandida, que es de la forma string1 = string2. El =operador es la comparación de cadenas de bash operador de . Entonces...
  3. Haga una comparación de cadenas en "true"y "true".
  4. Sí, las dos cadenas eran iguales, por lo que el valor del condicional es verdadero.
  5. Ejecute la cláusula de la ifdeclaración then.

El código sin corchetes funciona, porque el truecomando devuelve un código de salida de 0, que indica éxito. El código entre corchetes funciona, porque el valor de $the_world_is_flates idéntico al literal de cadena trueen el lado derecho de =.

Solo para llevar el punto a casa, 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". El comando de reinicio no se llama.

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 =, por lo que he visto, funcionan exactamente igual en todos los contextos.

Nótese, sin embargo, que estoy hablando específicamente acerca de las =y ==los operadores de comparación de cadena utilizadas por cualquiera [ ]o [[ ]]pruebas. No estoy sugiriendo eso =y ==son intercambiables en todas partes en bash.

Por ejemplo, obviamente no puede hacer una asignación variable con ==, como var=="foo"(bueno, técnicamente puede hacer esto, pero el valor de varserá "=foo", porque Bash no está viendo un ==operador aquí, está viendo un =operador (asignación), seguido de el valor literal ="foo", que simplemente se convierte"=foo" ).

También, aunque =y ==son intercambiables, usted debe tener en cuenta que la forma en esas pruebas de trabajo no depende de si se está usando en el interior [ ]o [[ ]], y también de si o no los operandos se citan. Puede leer más sobre eso en la Guía avanzada de secuencias de comandos Bash: 7.3 Otros operadores de comparación (desplácese hacia abajo hasta la discusión de =y ==).

Mike Holt
fuente
El enfoque sin corchetes también tiene la ventaja de permitirle escribir $the_world_is_flat && echo "you are in flatland!"
frases
99
Cierto. Aunque, no estoy abogando por (o en contra) de ninguno de los enfoques. Solo quería aclarar algo de la información errónea que se está votando aquí, para que las personas que tropiecen con este tema más adelante no salgan con un montón de ideas falsas sobre cómo funciona todo esto.
Mike Holt
1
La razón de la confusión es que la respuesta original de miku se mantuvo durante 4 años. Todas las referencias a la construcción truese hicieron con respecto a la respuesta original. (La respuesta revisada el 12 de febrero de 2014 no fue presentada por miku.) He editado la respuesta para incluir tanto original como revisado. Entonces los comentarios de las personas tienen sentido.
wisbucky
1
Al leer las respuestas ofrecidas aquí, tengo la impresión de que no existe tal cosa como usar realmente lo real true. ¿Hay alguna manera? Sospecho que muchos programadores que están acostumbrados a idiomas más estrictos que ven esta respuesta para ayudarlos a mezclar un poco de bashpegamento para facilitarles un poco la vida querrían un ===operador para que las cadenas y los "booleanos" no sean realmente intercambiables. En caso de que sólo se adhieren a 0 y 1 y su uso (( $maybeIAmTrue ))como se sugiere en Quolonel pregunta 's respuesta ?
SeldomNeedy
2
Para abordar el comentario de SeldomNeedy, sí, puede usar lo real true, pero generalmente no como algo para comparar una variable, ya que lo real trueno tiene valor per se. Todo lo que hace es establecer el estado de salida en 0, lo que indica éxito. Vale la pena señalar que es esencialmente equivalente al llamado "comando nulo", o :. En cuanto a usar 0y 1, eso es lo que hago en todos mis scripts en estos días donde necesito booleanos. Y uso el (( ))operador en lugar de [[ ]]evaluar. Entonces, por ejemplo, si tengo flag=0, entonces puedo hacerloif (( flag )); then ...
Mike Holt el
57

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:

verdadero
no falso

Preguntas de quolonel
fuente
3
Pros: (1.) el comportamiento es similar a la forma en que C maneja los bools, (2.) la sintaxis es muy concisa / mínima (no requiere una variable a la derecha y operadores como '=' o '=='), (3 .) <subjective> para mí entiendo lo que sucede sin una explicación larga y sin aliento ... contraste con las respuestas de Miku y Dennis que parecen requerir explicaciones largas y sin aliento </subjective>
Trevor Boyd Smith
3
@TrevorBoydSmith ¿Por qué no dijiste simplemente, "pros: todo, contras: nada". Ahorraría costos de depreciación en su teclado y monitor a largo plazo.
Preguntas de Quolonel
44
Para uso interactivo, como líneas simples, asegúrese de dejar un espacio después !, o se expandirá el historial. ((! foo))funciona, también lo hace ! ((foo)). Me encanta esta solución, por cierto. Finalmente una forma concisa de hacer variables booleanas. ((foo || bar))Funciona como se esperaba.
Peter Cordes
55
(())expande las variables de forma recursiva, lo que no esperaba. foo=bar; bar=baz; ((foo)) && echo echono imprime nada, pero es cierto con baz=1. Para que pueda apoyar foo=truey foo=falsetambién 0 o 1 haciendo true=1.
Peter Cordes
2
@quolonel gracias por el recurso tan útil. Por supuesto, mi comprensión es limitada: la naturaleza humana es limitada en toda comprensión, sin importar el dominio. Sin embargo, ¿le importaría decirme cuál de mis declaraciones lo lleva a suponer que mi comprensión de este asunto en particular es incompleta?
Hubert Grzeskowiak
35

Larga historia corta:

No hay booleanos en Bash

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.

Donde sea que vea trueo falseen Bash, es una cadena o un comando / incorporado 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 devuelve el código de salida 0. truey falseson Bash incorporados y, a veces, también programas independientes que no hacen nada más que devolver el código de salida correspondiente.

El condicional anterior es equivalente a:

COMMAND && ...

Cuando utiliza corchetes o el testcomando, confía en el código de salida de esa construcción. Tenga en cuenta que [ ]y [[ ]]también son solo comandos / incorporados como cualquier otro. Entonces ...

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

corresponde a

if COMMAND; then echo yes; fi

y el COMMANDaquí es[[ 1 == 1 ]]

La if..then..ficonstrucción es solo azúcar sintáctica. Siempre puede ejecutar los comandos separados por un doble ampersand para el mismo efecto:

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

Al usar truey falseen 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 esas condiciones están dando el mismo resultado :

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

TL; DR; siempre compara con cadenas o números

Para dejar esto en claro a los futuros lectores, recomendaría siempre usar comillas truey 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. It can be used for strict checking of dangerous operations. This condition is false for anything but the literal string "true".
Hubert Grzeskowiak
fuente
Prefiero usar Ty Fdejar en claro que esos no son valores booleanos reales.
phk
1
No puedo estar de acuerdo con "siempre usar corchetes dobles en bash". De hecho, en casi todos los scripts que he escrito estoy usando paréntesis, excepto cuando necesito hacer una coincidencia de patrones. Creo que uno debe entender la diferencia entre [(es decir test) [[y usar el que sea adecuado para su necesidad.
Weijun Zhou
@WeijunZhou ¿te importaría elaborar en qué casos los corchetes individuales son mejores?
Hubert Grzeskowiak
Es más un gusto personal, solo encuentro que es demasiado audaz decir "Usa siempre corchetes dobles en bash". Pero hay algunos casos extremos que he usado. Los corchetes individuales le permiten especificar la prueba en sí misma en una var. Como un ejemplo demasiado simplificado, considereif ....; then mytest='-gt'; else mytest='-eq'; fi; #several lines of code; if [ "$var1" "$mytest" "$var2" ]; then ...; fi
Weijun Zhou
1
@WeijunZhou Su ejemplo es un fuerte argumento en contra de corchetes individuales. Hace que el código sea mucho más difícil de entender y abre la ventana abierta a errores. Los corchetes dobles son más estrictos y fomentan un código más limpio.
Hubert Grzeskowiak
18

Hace mucho tiempo, cuando todo lo que teníamos era sh, los booleanos se manejaban confiando en una convención del testprograma donde se testdevuelve un estado de salida falso si se ejecuta sin ningún argumento.

Esto le permite a uno pensar en una variable que no está configurada como falsa y que la variable está configurada en cualquier valor como verdadero. Hoy en día, testestá integrado en Bash y es conocido comúnmente por su alias de un solo carácter [(o un ejecutable para usar en proyectiles que no lo tienen, como señala 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 escritores de guiones prefieren usar el comando compuesto [[que imita test, pero tiene una sintaxis más agradable: las variables con espacios no necesitan ser citadas; uno 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 ser confuso cuando se necesitan espacios, cadenas de longitud cero y variables nulas y también cuando su script necesita trabajar con varios shells.

Hbar
fuente
3
[no es solo un alias por dentro bash. Este alias también existe como un archivo binario (o como un enlace que apunta a) y se puede usar con el bare sh. Compruebe ls -l /usr/bin/\[. Con bash/ zshdeberías usarlo, [[es un verdadero interno puro y mucho más poderoso.
dolmen
1
@dolmen [y testtambién es un Bash SHELL BUILTIN COMMAND de acuerdo con la página del manual de Bash, por lo que no debería haber un problema en el rendimiento. Lo mismo con, por ejemplo, Dash. (/ bin / sh puede ser solo un enlace simbólico a / bin / dash). Para usar el ejecutable tienes que usar la ruta completa, es decir /usr/bin/\[.
jarno
12

¿Cómo puedo declarar y usar variables booleanas en un 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 ninguna variable booleana en Bash.

Sin embargo:

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

#!/bin/bash
declare -ir BOOL=(0 1) # Remember BOOL can't be unset till this shell terminates
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 ropción en declarey readonlyse usa para indicar explícitamente que las variables son de solo lectura . Espero que el propósito sea claro.

sjsam
fuente
1
¿Por qué no solo lo haces declare -ir false=0 true=1? ¿Cuál es la ventaja de usar una matriz?
Benjamin
@BenjaminW. Solo quería mencionar sobre la ropción y el readonlycomando. Lo haría de la manera que sugirió en mis scripts
sjsam el
tal vez me perdí algo, pero ¿por qué no declarar verdadero y falso de esta manera utiliza el signo de dólar? $ true $ false
qodeninja
Literalmente solo copiando mi respuesta y empeorando las cosas.
Preguntas del Quolonel
@QuolonelQuestions Las variables Bash no están escritas , por lo tanto, no tiene sentido decir. declare and use boolean variablesPodríamos, en más de una forma, imitar / asumir que una variable tiene un tipo . No vi eso mencionado en ninguna parte de su respuesta.
sjsam
10

En lugar de fingir un booleano y dejar una trampa para futuros lectores, ¿por qué no usar 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
Pirolista
fuente
¿Por qué no enteros?
phil294
3
@Blauhirn porque los enteros se usan de manera diferente según los idiomas. En algunos idiomas 0obliga a falsey 1hacia true. En lo que respecta a los códigos de salida del programa (que bash usa históricamente) es 0para un resultado positivo o truey todo lo demás es negativo / error o false.
Hubert Grzeskowiak
7

POSIX (interfaz del sistema operativo portátil)

Echo de menos 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 demasiado específicas de Bash .

Básicamente, solo deseo agregar más información sobre portabilidad.


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

    test "$var" = true && yourCodeIfTrue || yourCodeIfFalse

    Nota importante: ya no recomiendo esto, ya que se está desaprobando lentamente y es más difícil combinar varias declaraciones.

  2. Imagínese lo que esas palabras truey falsedecir a la cáscara, prueba 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 hagas una idea de lo bueno que es usar palabras clave adecuadas sin comillas .

    Pero nadie lo dijo en respuestas anteriores.

  3. ¿Qué significa esto? Bueno, varias cosas.

    • Debería acostumbrarse a que las palabras clave booleanas se traten como números, es decir true= 0y false= 1, recuerde que todos los valores distintos de cero se tratan como false.

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

      var_bool=true
      echo "$var_bool"
       true

      puedes crear un valor opuesto con:

      var_bool=$(( 1 - $var_bool ))  # same as $(( ! $var_bool ))
      echo "$var_bool"
      1

    Como puede ver por sí mismo, el shell imprime la truecadena por primera vez que lo usa, pero desde entonces, todo funciona a través de un número que 0representa trueo 1representa false, respectivamente.


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

  • Primero, un buen hábito sería asignar en 0lugar de true; 1en lugar de false.

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

    if [ "$var_bool" -eq 0 ]; then
         yourCodeIfTrue
    else
         yourCodeIfFalse
    fi
LinuxSecurityFreak
fuente
6

Con respecto a la sintaxis, esta es una metodología simple que uso (por ejemplo) para administrar lógica booleana de manera consistente y sensata:

# Tests
var=
var=''
var=""
var=0
var=1
var="abc"
var=abc

if [[ -n "${var}" ]] ; then
    echo 'true'
fi
if [[ -z "${var}" ]] ; then
    echo 'false'
fi

# Results
# var=        # false
# var=''      # false
# var=""      # false
# var=0       # true
# var=1       # true
# var="abc"   # true
# var=abc     # true

Si la variable nunca se declara, la respuesta es: # false

Entonces, una forma simple de establecer una variable en verdadero (usando esta metodología de sintaxis) sería var=1; por el contrario, var=''.

Referencia:

-n = Verdadero si la longitud de la cadena var no es cero.

-z = Verdadero si la longitud de la cadena var es cero.

Eric P
fuente
5

En muchos lenguajes de programación, el tipo booleano es, o se implementa como, un subtipo de entero, donde se truecomporta como 1y se falsecomporta como 0:

Matemáticamente , el álgebra booleana se parece al módulo aritmético de enteros 2. Por lo tanto, si un idioma no proporciona el tipo booleano nativo, la solución más natural y eficiente es usar enteros. Esto funciona con casi cualquier idioma. Por ejemplo, en Bash puedes hacer:

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

hombre bash :

((expresión))

La expresión se evalúa de acuerdo con las reglas que se describen a continuación en 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".

Cyker
fuente
5

Bill Parker está siendo rechazado , porque sus definiciones se invierten de la convención de código normal. Normalmente, verdadero se define como 0 y falso 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 cualquier cosa que no sea cero es un error. Lo siento, todavía no tengo la credibilidad de la calle para votar o responderle directamente.

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

Por un lado, -eqes un operador numérico, por lo que tener 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 dos corchetes a su alrededor, no emitirá una declaración de error, pero arrojará un valor incorrecto (bueno, en el 50% de las posibles permutaciones). Se evaluará a [[0 -eq verdadero]] = éxito, pero también a [[0 -eq falso]] = éxito, lo cual es incorrecto (hmmm ... ¿qué hay de que ese valor incorporado sea 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 un resultado incorrecto. Básicamente, cualquier cosa (que no sea la condición de error mencionada anteriormente) que establece una variable en un valor numérico y la compara con un valor verdadero / falso, o establece una variable con un valor verdadero / falso y la compara con un valor numérico. Además, cualquier cosa que establezca una variable en verdadero / falso incorporado y haga una comparación usando -eq. Por lo tanto, evite -eqlas comparaciones booleanas y evite usar 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 los componentes verdaderos / falsos tanto para su comparación como para sus evaluaciones (como señaló Mike Hunt, no las incluya entre comillas). Luego use un signo igual o simple o doble (= o ==) y corchetes simples o dobles ([] o [[]]). Personalmente, me gusta el signo de doble igual, porque me recuerda las comparaciones lógicas en otros lenguajes de programación y las 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.

Randyman99
fuente
2
Los true/ falseincorporados no se usan aquí (ignore lo que podría implicar el resaltado de sintaxis de algunos editores), especialmente en los […]casos en que puede considerarlo aquí como una cadena simple (una que se proporciona como parámetro del [comando).
phk
Lo tienes ahora.
Peter Mortensen
4

Mis hallazgos y sugerencias difieren un poco de las otras publicaciones. Descubrí que podía usar "booleanos" básicamente como lo haría en cualquier lenguaje "normal", sin el "salto de aro" sugerido ...

No hay necesidad de []comparaciones explícitas de cadenas ... Intenté múltiples distribuciones de Linux. Probé Bash, Dash y BusyBox . Los resultados fueron siempre los mismos. No estoy seguro de qué están hablando las publicaciones originales más votadas. ¿Quizás los tiempos han cambiado y eso es todo?

Si establece una variable en true , posteriormente se evalúa como "afirmativa" dentro de un condicional. Póngalo en false, y se evalúa como "negativo". Muy sencillo! ¡La única advertencia es que una variable indefinida también se evalúa como verdadera ! Sería bueno si hiciera lo contrario (como lo haría en la mayoría de los idiomas), pero ese es el truco: solo necesita inicializar explícitamente sus booleanos en verdadero o falso .

¿Por qué funciona de esta manera? Esa respuesta es doble. A) verdadero / falso en un shell realmente significa "sin error" frente a "error" (es decir, 0 frente a cualquier otra cosa). B) verdadero / falso no son valores, sino más bien declaraciones en scripts de shell. Con respecto al segundo punto, ejecutar trueo falseen una línea por sí mismo establece el valor de retorno para el bloque en el que se encuentra ese valor, falsees decir, es una declaración de "error encontrado", donde el verdadero "borra" eso. Usarlo con una asignación a una variable "devuelve" eso a la variable. Una variable indefinida se evalúa como trueen un condicional porque representa igualmente 0 o "no se encontró ningún error".

Vea el ejemplo de líneas bash y resultados a continuación. Pruébelo usted mismo si desea confirmar ...

#!/bin/sh

# Not yet defined...
echo "when set to ${myBool}"
if ${myBool}; then echo "it evaluates to true"; else echo "it evaluates to false"; fi;

myBool=true
echo "when set to ${myBool}"
if ${myBool}; then echo "it evaluates to true"; else echo "it evaluates to false"; fi;

myBool=false
echo "when set to ${myBool}"
if ${myBool}; then echo "it evaluates to true"; else echo "it evaluates to false"; fi;

Rendimientos

when set to
it evaluates to true
when set to true
it evaluates to true
when set to false
it evaluates to false
BuvinJ
fuente
1

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
Harsimranjit Singh Kler
fuente
1

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"
llundin
fuente
Sí, la descomposición funcional es poco apreciada.
Peter Mortensen
1

Las respuestas existentes me parecieron 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 felices, probé:

snapshotEvents=false

if !($snapshotEvents)
then
    # Do stuff if false
fi

Lo que también funcionó bien.

El $snapshotEventsevalúa los contenidos de valor de la variable. Entonces necesitas el $.

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

será
fuente
2
Cuando eliminas los paréntesis, esta es exactamente la respuesta original de @ miku en la parte superior.
dolmen
1
Sin paréntesis, la expresión no evalúa.
será
@ sí lo hará. No necesita el () s.
phil294
1
@Blauhirn ... Hola, basé mis comentarios en experimentos con GNU Bash en una PC Linux Mint / Ubuntu. Probablemente tengas razón en teoría () , no son necesarios. Mi única respuesta es probarlo, parece depender de la versión de Bash, la expresión o contexto real y tal.
será el
1

Aquí hay una mejora en la respuesta original de miku que aborda las preocupaciones de Dennis Williamson sobre el caso en el que no se establece la variable:

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

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

No tiene que afectar el rendimiento del programa haciéndolo en cada uso de la variable como sugiere Dennis Williamson .

dolmen
fuente
1

Puedes usar shFlags .

Te da la opción de definir: DEFINE_bool

Ejemplo:

DEFINE_bool(big_menu, true, "Include 'advanced' options in the menu listing");

Desde la línea de comando puede definir:

sh script.sh --bigmenu
sh script.sh --nobigmenu # False
gogasca
fuente
GFlags no tiene sentido en esta respuesta: es una biblioteca C ++. No se puede usar directamente en scripts de shell.
Jonathan Cross
Respuesta actualizada a shFlags, que es un puerto de GFlags para shell.
Gogasca
0

Esta es una prueba de velocidad sobre diferentes formas de probar los valores "booleanos" en Bash:

#!/bin/bash
rounds=100000

b=true # For true; b=false for false
type -a true
time for i in $(seq $rounds); do command $b; done
time for i in $(seq $rounds); do $b; done
time for i in $(seq $rounds); do [ "$b" == true ]; done
time for i in $(seq $rounds); do test "$b" == true; done
time for i in $(seq $rounds); do [[ $b == true ]]; done

b=x; # Or any non-null string for true; b='' for false
time for i in $(seq $rounds); do [ "$b" ]; done
time for i in $(seq $rounds); do [[ $b ]]; done

b=1 # Or any non-zero integer for true; b=0 for false
time for i in $(seq $rounds); do ((b)); done

Imprimiría algo como

true is a shell builtin
true is /bin/true

real    0m0,815s
user    0m0,767s
sys     0m0,029s

real    0m0,562s
user    0m0,509s
sys     0m0,022s

real    0m0,829s
user    0m0,782s
sys     0m0,008s

real    0m0,782s
user    0m0,730s
sys     0m0,015s

real    0m0,402s
user    0m0,391s
sys     0m0,006s

real    0m0,668s
user    0m0,633s
sys     0m0,008s

real    0m0,344s
user    0m0,311s
sys     0m0,016s

real    0m0,367s
user    0m0,347s
sys     0m0,017s
jarno
fuente
-2

Alternativa: use una función

is_ok(){ :;}
is_ok(){ return 1;}
is_ok && echo "It's OK" || echo "Something's wrong"

Definir la función es menos intuitivo, pero verificar su valor de retorno es muy fácil.

johnraff
fuente
1
Esto no es una variable que se puede probar, pero una función constante
jarno
@jarno ¿La prueba del valor de retorno de una función es diferente de la prueba de una variable, a los efectos de un script?
johnraff
Bueno, la pregunta es sobre variables.
jarno
Es cierto, aunque el uso en un script de shell sería el mismo.
johnraff
-2

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

Todos pisando los espacios de código de los demás. Supongo que esto es principalmente histórico, donde Bash tuvo que fingir que era de shvez en cuando.

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

TRUE=1; FALSE=0

Entonces puedo usar el ((...)) operador aritmético para probar de esta manera.

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. Tutestvar debe ser bien sea a $TRUEo $FALSEen todo momento.

  2. En (( ... ))comparadores, no necesita lo anterior $, 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 un $ ocasionalmente:

    testvar=$TRUE

    que no es tan bonito

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

Bill Parker
fuente
2
Debe declarar sus constantes de solo lectura. Además, utilice siempre llaves cuando use variables. Es una convención que todos deberían cumplir con mi humilde opinión. La gran desventaja de esta solución es que no puede mezclar la expresión algebraica con indicadores de prueba o comparaciones de cadenas.
Hubert Grzeskowiak