Question Comment déclarer et utiliser des variables booléennes dans un script shell?


J'ai essayé de déclarer une variable booléenne dans un script shell en utilisant la syntaxe suivante:

variable=$false

variable=$true

Est-ce correct? Aussi, si je voulais mettre à jour cette variable j'aurais utiliser la même syntaxe? Enfin, la syntaxe suivante est-elle utilisée pour utiliser des variables booléennes comme expressions correctes:

if [ $variable ]

if [ !$variable ]

649
2018-06-01 21:54


origine


Réponses:


Réponse révisée (12 février 2014)

the_world_is_flat=true
# ...do something interesting...
if [ "$the_world_is_flat" = true ] ; then
    echo 'Be careful not to fall off!'
fi

Réponse originale

Mises en garde: 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: Utilisation de variables booléennes dans Bash

La raison pour laquelle la réponse originale est incluse ici est parce que les commentaires avant la révision du 12 février 2014 ne concernent que la réponse originale, et beaucoup de commentaires sont faux lorsqu'ils sont associés à la réponse révisée. Par exemple, le commentaire de Dennis Williamson à propos de bash builtin true le 2 juin 2010 ne s'applique qu'à la réponse originale, pas à la version révisée.


815
2018-06-01 21:58



TL; DR

bool=true

if [ "$bool" = true ]

Problèmes avec Miku (original) répondre

je fais ne pas recommander la réponse acceptée1. Sa syntaxe est jolie mais elle a quelques défauts.

Disons que nous avons la condition suivante.

if $var; then
  echo 'Muahahaha!'
fi

Dans les cas suivants2, cette condition va évaluer à vrai et exécutez la commande imbriquée.

# 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

Généralement vous voulez seulement que votre condition évalue à vrai quand votre variable "booléenne", var dans cet exemple, est explicitement défini sur true. Tous les autres cas sont dangereusement trompeurs!

Le dernier cas (# 5) est particulièrement vilain car il va exécuter la commande contenue dans la variable (c'est pourquoi la condition évalue à vrai pour les commandes valides3, 4).

Voici un exemple inoffensif:

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!

La citation de vos variables est plus sûre, par ex. if "$var"; then. Dans les cas ci-dessus, vous devriez recevoir un avertissement que la commande n'est pas trouvée. Mais nous pouvons encore faire mieux (voir mes recommandations en bas).

Voir aussi l'explication de Mike Holt de la réponse originale de Miku.

Problèmes avec la réponse de Hbar

Cette approche a également un comportement inattendu.

var=false
if [ $var ]; then
  echo "This won't print, var is false!"
fi

# Outputs:
# This won't print, var is false!

Vous vous attendez à ce que la condition ci-dessus soit évaluée à false, donc n'exécutant jamais l'instruction imbriquée. Surprise!

Citant la valeur ("false"), en citant la variable ("$var"), ou en utilisant test ou [[ au lieu de [, ne faites pas de différence.

Ce que je recommande:

Voici des façons que je vous recommande de vérifier vos "booléens". Ils fonctionnent comme prévu.

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

Ils sont tous à peu près équivalents. Vous devrez taper quelques touches de plus que les approches dans les autres réponses5 mais votre code sera plus défensif.


Notes de bas de page

  1. La réponse de Miku a depuis été éditée et ne contient plus de défauts (connus).
  2. Pas une liste exhaustive.
  3. Une commande valide dans ce contexte signifie une commande qui existe. Peu importe si la commande est utilisée correctement ou incorrectement. Par exemple. man woman serait toujours considéré comme une commande valide, même si aucune page de manuel n'existe.
  4. Pour les commandes invalides (inexistantes), Bash se plaindra simplement que la commande n'a pas été trouvée.
  5. Si vous vous souciez de la longueur, la première recommandation est la plus courte.

595
2018-01-18 22:57



Il semble y avoir un malentendu ici à propos de la bash builtin true, et plus spécifiquement, sur la façon dont bash développe et interprète les expressions entre parenthèses.

Le code dans la réponse de miku n'a absolument rien à voir avec le bash intégré true, ni /bin/true, ni aucune autre saveur de true commander. Dans ce cas, true est rien de plus qu'une simple chaîne de caractères, et aucun appel à la true command / builtin est jamais fait, ni par l'assignation de variable, ni par l'évaluation de l'expression conditionnelle.

Le code suivant est fonctionnellement identique au code dans la réponse du miku:

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

le seulement La différence ici est que les quatre caractères comparés sont «y», «e», «a» et «h» au lieu de «t», «r», «u» et «e». C'est tout. Il n'y a aucune tentative faite pour appeler une commande ou builtin nommé yeah, et il n'y a pas (dans l'exemple de miku) de manipulation spéciale quand bash analyse le jeton true. C'est juste une chaîne, et une complètement arbitraire à cela.

Mise à jour (19/02/2014): Après avoir suivi le lien dans la réponse de Miku, je vois maintenant d'où vient la confusion. La réponse de Miku utilise des crochets simples, mais l'extrait de code auquel il est lié n'utilise pas de parenthèses. C'est juste:

the_world_is_flat=true
if $the_world_is_flat; then
  echo 'Be careful not to fall off!'
fi

Les deux extraits de code seront se comporter de la même manière, mais les supports changent complètement ce qui se passe sous le capot.

Voici ce que fait bash dans chaque cas:

Pas de parenthèses:

  1. Développez la variable $the_world_is_flat à la chaîne "true".
  2. Essayez d'analyser la chaîne "true" comme une commande.
  3. Trouvez et exécutez le true commande (soit un intégré ou /bin/true, selon la version de bash).
  4. Comparez le code de sortie du true commande (qui est toujours 0) avec 0. Rappelez-vous que dans la plupart des shells, un code de sortie de 0 indique le succès et toute autre chose indique un échec.
  5. Comme le code de sortie était 0 (succès), exécutez le if déclaration then clause

Supports:

  1. Développez la variable $the_world_is_flat à la chaîne "true".
  2. Analyser l'expression conditionnelle maintenant entièrement développée, qui est de la forme string1 = string2. le = l'opérateur est bash comparaison de chaînes opérateur. Alors...
  3. Faites une comparaison de chaînes sur "true" et "true".
  4. Oui, les deux chaînes étaient identiques, donc la valeur du conditionnel est vraie.
  5. Exécuter le if déclaration then clause.

Le code sans parenthèses fonctionne parce que le true commande renvoie un code de sortie de 0, ce qui indique le succès. Le code entre crochets fonctionne parce que la valeur de $the_world_is_flat est identique à la chaîne littérale true sur le côté droit de la =.

Juste pour ramener le point à la maison, considérons les deux extraits de code suivants:

Ce code (s'il est exécuté avec des privilèges root) va redémarrer votre ordinateur:

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

Ce code imprime simplement "Nice try". La commande reboot n'est pas appelée.

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

Mise à jour (14/04/2014) Pour répondre à la question dans les commentaires concernant la différence entre = et ==: AFAIK, il n'y a pas de différence. le == opérateur est un synonyme spécifique à bash pour =et, pour autant que je l'ai vu, ils fonctionnent exactement de la même manière dans tous les contextes. Notez, cependant, que je parle spécifiquement de la = et == opérateurs de comparaison de chaînes utilisés dans [ ] ou [[ ]] tests. Je ne suggère pas que = et == sont interchangeables partout à bash. Par exemple, vous ne pouvez évidemment pas faire d'assignation de variable avec ==, tel que var=="foo" (bien techniquement vous pouvez faites cela, mais la valeur de var sera "=foo", parce que bash ne voit pas == opérateur ici, il voit un = opérateur (affectation), suivi de la valeur littérale ="foo", qui devient juste "=foo").

Aussi, bien que = et == sont interchangeables, vous devez garder à l'esprit que la façon dont ces tests fonctionnent Est-ce que dépendez-vous si vous l'utilisez à l'intérieur [ ] ou [[ ]], et aussi si les opérandes sont ou non cités. Vous pouvez en lire plus à ce sujet ici: Advanced Bash Scripting Guide: 7.3 Autres opérateurs de comparaison (faites défiler jusqu'à la discussion de = et ==).


99
2018-02-13 23:45



Utilisez des expressions arithmétiques.

#!/bin/bash

false=0
true=1

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

Sortie:

vrai
    pas faux


40
2017-11-13 23:44



Il y a longtemps, quand tout ce que nous avions était sh, les booléens où manipulé en s'appuyant sur une convention de la test programme où test renvoie un faux état de sortie s'il est exécuté sans argument. Cela permet de penser à une variable qui n'est pas définie comme étant fausse et variable à n'importe quelle valeur comme vraie. Aujourd'hui, le test est intégré à bash et est généralement connu par son alias un caractère [ (ou un exécutable à utiliser dans les shells qui en manquent, comme le note 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

En raison des conventions de citation, les scénaristes préfèrent utiliser la commande composée [[ qui imite test mais a une syntaxe plus agréable: les variables avec des espaces n'ont pas besoin d'être citées, on peut utiliser && et || en tant qu'opérateurs logiques avec une préséance bizarre, et il n'y a pas de limitations POSIX sur le nombre de termes.

Par exemple, pour déterminer si FLAG est défini et COUNT est un nombre supérieur à 1:

FLAG="u p"
COUNT=3

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

Ce genre de choses peut prêter à confusion lorsque des espaces, des chaînes de longueur nulle et des variables nulles sont nécessaires et que votre script doit fonctionner avec plusieurs shells.


14
2018-04-10 20:23



Comment déclarer et utiliser des variables booléennes dans un script shell?

Contrairement à beaucoup d'autres langages de programmation, Bash ne sépare pas ses variables par "type". [1]

Donc, la réponse est assez claire. Il n'y a pas boolean variable à bash. Toutefois :

À l'aide d'une déclaration, nous pouvons limiter l'affectation de valeur aux 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"

le r option dans declare et readonly est utilisé pour indiquer explicitement que les variables sont lecture seulement. Espérons que le but est clair.


11
2018-04-30 06:19



Au lieu de simuler un booléen et de laisser un piège pour les futurs lecteurs, pourquoi ne pas simplement utiliser une meilleure valeur que vrai et faux?

Par exemple:

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



Longue histoire courte:

Vous n'êtes pas des booléens à bash

Ce que bash a, c'est des expressions booléennes en termes de comparaison et de conditions. Cela dit, ce que vous pouvez déclarer et comparer dans bash sont des chaînes et des nombres. C'est tout.

Où que vous voyez true ou false dans bash, c'est une chaîne ou une commande / builtin qui est seulement utilisée pour son code de sortie.

Cette syntaxe ...

if true; then ...

est essentiellement ...

if COMMAND; then ...

La condition est vraie chaque fois que la commande renvoie le code de sortie 0.

Vous pourriez aussi bien faire ceci:

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

Lorsque vous utilisez des crochets ou test commande, vous comptez sur le code de sortie de cette construction. Garde en tête que [ ] et [[ ]] sont aussi juste des commandes / builtins comme les autres. Alors ...

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

est juste du sucre syntaxique pour ...

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

Donc, quand vous utilisez true et false dans l'une des constructions susmentionnées, vous ne faites que passer la chaîne "true" ou "false" à la commande de test. Voici un exemple:

Croyez-le ou non, mais ces conditions donnent toutes même résultat:

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

TL; DR; comparer toujours par rapport aux chaînes ou aux nombres

Pour le dire clairement aux futurs lecteurs, je recommanderais toujours d'utiliser des citations autour true et false:

FAIRE

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

NE PAS

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 `==`

Peut être

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 (Interface du système d'exploitation portable)

Il me manque ici le point clé, qui est la portabilité. C'est pourquoi mon en-tête a POSIX en soi.

Essentiellement, toutes les réponses votées sont correctes, sauf qu'elles sont FRAPPER-spécifique trop.

Donc, fondamentalement, je souhaite seulement ajouter plus d'informations sur la portabilité.


  1. [ et ] crochets comme dans [ "$var" = true ] ne sont pas nécessaires, et vous pouvez les omettre et utiliser le test commande directement:

    test "$var" = true && CodeIfTrue || CodeIfFalse
    
  2. Imaginez ce que ces mots true et false signifie à la coquille, tester vous-même:

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

    Mais en utilisant des citations:

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

    C'est la même chose pour:

    echo $(("false"))
    

    Le shell ne peut pas l'interpréter autrement qu'une chaîne. J'espère que vous obtenez l'idée de la façon dont bon utilise le mot-clé approprié sans pour autant citations

    Mais personne ne l'a dit dans les réponses précédentes.

  3. Qu'est-ce que cela signifie? Eh bien, plusieurs choses.

    • Vous devriez vous habituer aux mots-clés booléens qui sont traités comme des nombres, c'est-à-dire true = 0 et false = 1, rappelez-vous que toutes les valeurs non nulles sont traitées comme false.

    • Puisqu'ils sont traités comme des nombres, vous devriez les traiter comme cela aussi, c'est-à-dire si vous définissez une variable:

      var_a=true
      echo "$var_a"
      
       true
      

      vous pouvez en créer une valeur opposée avec:

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

      Comme vous pouvez le voir par vous-même, la coque est imprimée true chaîne pour la première fois que vous l'utilisez, mais depuis lors, tout fonctionne par numéro 0 ou 1, respectivement.


Enfin, que devriez-vous faire avec toutes ces informations

  • Première bonne habitude serait d'attribuer 0 au lieu de true; 1 au lieu de false.

  • La deuxième bonne habitude serait de tester si la variable est / n'est pas égale à zéro:

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

2
2018-02-01 17:45



Bill Parker est rejeté parce que ses définitions sont inversées par rapport à la convention de code normale. Normalement, true est défini comme 0 et false est défini comme non nul. 1 fonctionnera pour faux, comme le fera 9999 et -1. Idem avec les valeurs de retour de la fonction - 0 est un succès et tout ce qui n'est pas nul est un échec. Désolé, je n'ai pas encore le droit de voter ou de lui répondre directement.

Bash recommande d'utiliser des doubles crochets maintenant comme une habitude au lieu de simples crochets, et le lien fourni par Mike Holt explique les différences dans leur fonctionnement. 7.3. Autres opérateurs de comparaison

Pour une chose-eq est un opérateur numérique, ayant ainsi le code

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

émet une instruction d'erreur, en attendant une expression entière. Cela s'applique à l'un ou l'autre paramètre, car ni l'un ni l'autre n'est une valeur entière. Pourtant, si nous mettons des doubles parenthèses autour de lui, il n'émettra pas une déclaration d'erreur, mais donnera une valeur erronée (bien, dans 50% des permutations possibles). Il va évaluer à [[0 -eq vrai]] = succès, mais aussi à [[0 -eq faux]] = succès, ce qui est faux (hmmm .... qu'en est-il de cette builtin étant une valeur numérique?).

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

Il y a d'autres permutations du conditionnel qui donneront aussi une mauvaise sortie. Fondamentalement, n'importe quoi (autre que la condition d'erreur énumérée ci-dessus) qui définit une variable à une valeur numérique et la compare à un builtin vrai / faux, ou définit une variable à un builtin vrai / faux et le compare à une valeur numérique. En outre, tout ce qui définit une variable à un builtin vrai / faux et fait une comparaison en utilisant -eq. Evitez donc -eq pour les comparaisons booléennes et évitez d'utiliser des valeurs numériques pour les comparaisons booléennes. Voici un résumé des permutations qui donneront des résultats invalides:

#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

Alors, maintenant à ce qui fonctionne. Utilisez true / false builtins pour votre comparaison et vos évaluations (comme l'a noté Mike Hunt, ne les mettez pas entre guillemets). Utilisez ensuite soit un signe égal unique ou double (= ou ==) et des parenthèses simples ou doubles ([] ou [[]]). Personnellement, j'aime le double signe égal parce qu'il me rappelle des comparaisons logiques dans d'autres langages de programmation, et des doubles guillemets juste parce que j'aime taper. Donc, cela fonctionne:

#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

Voilà.


1
2017-09-21 00:38



Voici une implémentation d'un 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
}

Exemple 1

my_boolean=true

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

Exemple 2 

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

1
2018-03-09 13:49