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, utiliserais-je la même syntaxe? Enfin, la syntaxe suivante permet d’utiliser les variables booléennes comme expressions correctes:

 if [ $variable ] if [ !$variable ] 

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 initiale est incluse ici est que les commentaires avant la révision du 12 février 2014 ne concernent que la réponse originale, et que bon nombre des commentaires sont erronés 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 d’origine, pas à la version révisée.

TL; DR

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

Problèmes avec la réponse ( originale ) de Miku

Je ne recommande pas la réponse acceptée 1 . Sa syntaxe est jolie mais elle a des défauts.

Disons que nous avons la condition suivante

 if $var; then echo 'Muahahaha!' fi 

Dans les cas suivants 2 , cette condition sera évaluée à true et exécutera la commande nestede.

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

Généralement, vous voulez que votre condition soit évaluée à true lorsque votre variable “booléenne”, var dans cet exemple, est définie explicitement sur true. Tous les autres cas sont dangereusement trompeurs!

Le dernier cas (# 5) est particulièrement désagréable car il exécutera la commande contenue dans la variable (ce qui explique pourquoi la condition est vraie pour les commandes valides 3, 4 ).

Voici un exemple sans danger:

 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! 

Citer vos variables est plus sûr, par exemple if "$var"; then if "$var"; then Dans les cas ci-dessus, vous devriez recevoir un avertissement indiquant que la commande est introuvable. Mais nous pouvons encore faire mieux (voir mes recommandations en bas).

Voir aussi l’explication de Mike Holt sur 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, n’exécutant ainsi jamais l’instruction nestede. Surprise!

En 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 comment je vous recommande de vérifier vos “booléens”. Ils travaillent 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 assez équivalents. Vous devrez taper quelques touches de plus que les approches des autres réponses 5, mais votre code sera plus défensif.


Notes de bas de page

  1. La réponse de Miku a depuis été modifiée et ne contient plus de failles (connues).
  2. Liste non 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, l’ man woman serait toujours considéré comme une commande valide, même s’il n’existe pas de page de manuel.
  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.

Il semble y avoir quelques malentendus à propos de la valeur true Bash, et plus précisément, de la manière dont Bash développe et interprète les expressions entre crochets.

Le code dans la réponse de miku n’a absolument rien à voir avec le true intégré de Bash, ni avec /bin/true , ni avec aucune autre version de la true commande. Dans ce cas, true n’est rien de plus qu’une simple chaîne de caractères, et aucun appel à la true commande / builtin n’est jamais effectué, ni par l’affectation de la 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 

La seule différence 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’ya aucune tentative d’appeler une commande ou une commande interne nommée yeah , et il n’ya pas non plus (dans l’exemple de miku) de traitement particulier lorsque Bash parsing le jeton true . C’est juste une chaîne, et complètement arbitraire.

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 crochets. 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 se comporteront de la même manière, mais les crochets changent complètement ce qui se passe sous le capot.

Voici ce que Bash fait dans chaque cas:

Pas de parenthèses:

  1. Développez la variable $the_world_is_flat à la chaîne "true" .
  2. Essayez d’parsingr la chaîne "true" tant que commande.
  3. Recherchez et exécutez la commande true (soit une commande intégrée, soit /bin/true , selon la version de Bash).
  4. Comparez le code de sortie de la commande true (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 que toute autre chose indique un échec.
  5. Comme le code de sortie était 0 (succès), exécutez la clause then l’instruction if

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 ssortingng1 = ssortingng2 . L’opérateur = est l’opérateur de comparaison de chaînes de bash. Alors…
  3. Faites une comparaison de chaîne sur "true" et "true" .
  4. Oui, les deux chaînes étaient les mêmes, donc la valeur du conditionnel est vraie.
  5. Exécutez la clause then l’instruction if .

Le code sans crochets fonctionne, car la commande true renvoie un code de sortie de 0, ce qui indique le succès. Le code entre parenthèses fonctionne, car la valeur de $the_world_is_flat est identique à la chaîne littérale true à droite du = .

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

Ce code (s’il est exécuté avec les privilèges root) redémarrera votre ordinateur:

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

Ce code affiche juste “Nice try”. La commande reboot n’est pas appelée.

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

Mise à jour (2014-04-14) Pour répondre à la question dans les commentaires concernant la différence entre = et == : AFAIK, il n’y a pas de différence. L’opérateur == est un synonyme spécifique de Bash pour = , et pour autant que je sache, ils fonctionnent exactement de la même manière dans tous les contextes.

Notez toutefois que je parle spécifiquement des opérateurs de comparaison de chaînes = et == utilisés dans les tests [ ] ou [[ ]] . Je ne suggère pas que = et == soient interchangeables partout dans bash.

Par exemple, vous ne pouvez évidemment pas faire l’affectation de variables avec == , comme var=="foo" (bien, techniquement vous pouvez le faire, mais la valeur de var sera "=foo" , car Bash ne voit pas de == opérateur ici, il voit un opérateur = (affectation), suivi de la valeur littérale ="foo" , qui devient simplement "=foo" ).

De plus, bien que = et == soient interchangeables, vous devez garder à l’esprit que le fonctionnement de ces tests dépend si vous l’utilisez à l’intérieur de [ ] ou [[ ]] , et si les opérandes sont citées ou non. Vous pouvez en savoir plus à ce sujet dans Advanced Bash Scripting Guide: 7.3 Autres opérateurs de comparaison (faites défiler jusqu’à la discussion de = et == ).

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

Il y a longtemps, lorsque tout ce que nous avions était sh , booléen était géré en utilisant une convention du programme de testtest retourne un statut de sortie faux s’il est exécuté sans arguments. Cela permet de penser à une variable non définie comme false et à une variable définie sur n’importe quelle valeur comme true. Aujourd’hui, le test est intégré à bash et est généralement connu sous son alias à un caractère [ (ou un exécutable à utiliser dans les shells qui en sont dépourvus, comme les notes de dolmen):

 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 

En raison des conventions de citation, les rédacteurs de scripts préfèrent utiliser la commande composée [[ qui imite le test mais possède 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 étrange précédence, 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 que COUNT est un nombre supérieur à 1:

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

Ces éléments peuvent être déroutants lorsque des espaces, des chaînes de longueur nulle et des variables nulles sont tous nécessaires, ainsi que lorsque votre script doit fonctionner avec plusieurs shells.

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

Contrairement à de nombreux 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 de boolean variable dans bash. Toutefois :

En utilisant une déclaration declare, nous pouvons limiter l’affectation des valeurs 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" 

L’option r de declare et readonly permet d’indiquer explicitement que les variables sont en lecture seule . J’espère que le but est clair.

Longue histoire courte:

Il n’y a pas de booléens en bash

Ce que bash a, ce sont 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 voyiez true ou false dans bash, c’est soit une chaîne, soit une commande / builtin qui n’est utilisée que 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 tout aussi bien faire ceci:

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

Lorsque vous utilisez des crochets ou la commande test , vous vous fiez au code de sortie de cette construction. Gardez à l’esprit que [ ] et [[ ]] sont que des commandes / construit comme les autres. Alors …

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

est juste du sucre syntaxique pour …

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

Ainsi, lorsque vous utilisez true et false dans l’une des constructions susmentionnées, vous ne transmettez en réalité que la chaîne "true" ou "false" à la commande testing. Voici un exemple:

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

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

TL; DR; toujours comparer à des chaînes ou des nombres

Pour que cela soit clair pour les futurs lecteurs, je recommande de toujours utiliser des guillemets autour de 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 ssortingct checking of dangerous operations. This condition is false for anything but the literal ssortingng "true". 

Au lieu de tromper un booléen et de laisser un piège aux futurs lecteurs, pourquoi ne pas simplement utiliser une valeur supérieure à celle du vrai et du 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 

POSIX (interface de 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, à l’exception du fait qu’elles sont spécifiques à BASH .

Donc, fondamentalement, je souhaite seulement append 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 directement la commande test :

     test "$var" = true && CodeIfTrue || CodeIfFalse 
  2. Imaginez ce que ces mots signifient true et false pour le shell, testez-le 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 comprenez à quel point il est bon d’utiliser un mot-clé correct sans les guillemets.

    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 .

    • Comme ils sont traités comme des nombres, vous devriez les traiter de la même manière, c.-à-d. Si vous définissez une variable, dites:

       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 constater par vous-même, shell imprime la true chaîne de caractères pour la première fois que vous l’utilisez, mais depuis, tout fonctionne via le numéro 0 ou 1 , respectivement.


Enfin, que faire de toutes ces informations?

  • La première bonne habitude serait d’atsortingbuer 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 

Bill Parker est rejeté , car ses définitions sont inversées par rapport à la convention de code normale. Normalement, true est défini sur 0 et false sur non-zéro. 1 travaillera pour faux, comme le seront 9999 et -1. Idem avec les valeurs de retour de fonction – 0 est un succès et tout ce qui est différent de zéro est un échec. Désolé je n’ai pas encore la crédibilité de la rue pour voter ou pour lui répondre directement.

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

D’une part, -eq est un opérateur numérique, donc avoir le code

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

émettra une instruction d’erreur, attend une expression entière. Cela s’applique à l’un ou l’autre paramètre, car aucun n’est une valeur entière. Cependant, si nous mettons des doubles crochets, cela ne produira pas d’énoncé d’erreur, mais cela donnera une mauvaise valeur (enfin, dans 50% des permutations possibles). Il évaluera à [[0 -eq true]] = succès, mais aussi à [[0 -eq false]] = succès, ce qui est faux (hmmm …. qu’en est-il de cette valeur numérique?).

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

Il existe d’autres permutations du conditionnel qui donneront également de mauvais résultats. Fondamentalement, n’importe quoi (autre que la condition d’erreur indiquée ci-dessus) qui définit une variable sur une valeur numérique et la compare à une valeur intégrée true / false ou définit une variable sur une valeur intégrée true / false et la compare à une valeur numérique. En outre, tout ce qui définit une variable à un paramètre intégré true / false et effectue une comparaison en utilisant -eq . Évitez 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 non valides:

 #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 à quoi fonctionne. Utilisez les méthodes intégrées true / false à la fois pour votre comparaison et pour vos évaluations (comme l’a noté Mike Hunt, ne les mettez pas entre guillemets). Utilisez ensuite un signe simple ou double ou égal (= ou ==) et des crochets simples ou doubles ([] ou [[]]). Personnellement, j’aime le signe double égal, car cela me rappelle les comparaisons logiques dans d’autres langages de programmation, et les guillemets simplement parce que j’aime bien taper. Donc, ces travaux:

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

Voici une mise en œuvre d’un raccourci 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" 

Voici un exemple simple qui fonctionne pour moi:

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

J’ai trouvé les réponses existantes déroutantes.

Personnellement, je veux juste avoir quelque chose qui ressemble et fonctionne comme C.

Cet extrait fonctionne plusieurs fois par jour en production:

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

et pour satisfaire tout le monde, j’ai testé:

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

Ce qui a aussi bien fonctionné.

$snapshotEvents évalue le contenu de la valeur de la variable. Donc, vous avez besoin du $ .

Vous n’avez pas vraiment besoin des parenthèses, je les trouve juste utiles.

  • Testé sur: GNU Bash, version 4.1.11 (2) -release

  • Guide pratique pour les débutants , Garrtelt Garrels, v1.11, 2008

Voici une amélioration de la réponse originale de miku, qui répond aux préoccupations de Dennis Williamson à propos de l’affaire, où la variable n’est pas définie:

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

Et pour tester si la variable est false :

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

En ce qui concerne les autres cas avec un contenu désagréable dans la variable, il s’agit d’un problème avec toute entrée externe fournie à un programme.

Toute entrée externe doit être validée avant de lui faire confiance. Mais cette validation doit être effectuée une seule fois lorsque cette entrée est reçue.

Il n’est pas nécessaire d’avoir un impact sur la performance du programme en le faisant à chaque utilisation de la variable, comme le suggère Dennis Williamson .

Utiliser le bon sens. Dans de nombreuses langues, 1 est vrai et 0 est faux. Heureusement, cela va aussi avec Bash.

Consortingbution:

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

Sortie:

 true false 

Source :

((expression))

L’expression est évaluée selon les règles décrites ci-dessous sous ÉVALUATION ARITHMÉTIQUE. Si la valeur de l’expression est différente de zéro, le statut de retour est 0; sinon le statut de retour est 1. Ceci est exactement équivalent à laisser “expression”.

Bash confond vraiment le problème avec les goûts de [ , [[ , (( , $(( , etc.

Tous traitent les uns des autres espaces de code. Je suppose que c’est surtout historique, où Bash devait faire semblant d’être de temps en temps.

La plupart du temps, je peux simplement choisir une méthode et la respecter. Dans ce cas, j’ai tendance à déclarer (de préférence dans un fichier de bibliothèque commun que je peux inclure avec . Dans mes scripts réels).

 TRUE=1; FALSE=0 

Je peux alors utiliser l’opérateur arithmétique (()) pour tester ainsi.

 testvar=$FALSE if [[ -d ${does_directory_exist} ]] then testvar=$TRUE; fi if (( testvar == TRUE )); then # do stuff because the directory does exist fi 
  1. Vous devez être discipliné. Votre testvar doit toujours être défini sur $TRUE ou $FALSE .

  2. Dans les comparateurs (()) , vous n’avez pas besoin du $ précédent, ce qui le rend plus lisible.

  3. Je peux utiliser (()) car $TRUE=1 et $FALSE=0 , c’est-à-dire des valeurs numériques.

  4. L’inconvénient est d’avoir à utiliser occasionnellement un $ :

     testvar=$TRUE 

    ce qui n’est pas si joli.

Ce n’est pas une solution parfaite, mais elle couvre tous les cas, j’ai besoin d’un tel test.