web-dev-qa-db-fra.com

Comprendre les opérateurs booléens dans le script bash

phone_missing=false
echo "missing $phone_missing"

if [ ! $phone_missing ]
then
        echo "Lost phone at $readabletime"
        $phone_missing=true
fi

Je ne peux pas comprendre ça. La ligne

echo "missing $phone_missing"

échos missing false, Je m'attendrais à la déclaration

if [ ! $phone_missing ]

être vrai et entrez la clause if, mais ce n'est pas le cas? Qu'est-ce que j'oublie ici!?

18
asco

La variable $phone_missing est une chaîne qui contient false. Et une chaîne non vide est évaluée à true. Voir aussi http://www.linuxintro.org/wiki/Babe#empty_strings

17
Thorsten Staerk

J'utilise souvent "vrai" et "faux" car ce sont aussi des commandes qui renvoient simplement le succès et l'échec respectivement. Ensuite, vous pouvez faire

if "$phone_missing"; then ...
16
glenn jackman

Voici une façon de procéder, tout en conservant les valeurs vrai/faux.

phone_missing=false
if [ "$phone_missing" != false ]; then
    echo "phone_missing is not 'false' (but may be non-true, too)"
fi
if [ "$phone_missing" == true ]; then
    echo "phone_missing is true."
fi

Les doubles guillemets autour de $phone_missing doivent se protéger contre le cas où la variable phone_missing n'est pas défini du tout. Un autre idiome courant pour éviter cela est [ x$phone_missing != xfalse ], mais les citations me semblent plus naturelles.

L'astuce se trouve dans la page d'aide de bash pour test:

  STRING      True if string is not empty.
  ...
  ! EXPR      True if expr is false.

Donc en gros [ $foo ] sera vrai si $foo n'est pas vide. Pas vrai ou faux, juste non vide. [ ! $foo ] est vrai si $ foo est vide ou indéfini.

Vous pouvez toujours changer votre code pour simplement définir phone_missing à une valeur non vide, qui indiquera vrai. Si phone_missing n'est pas défini (ou vide - phone_missing=""), ce sera faux. Sinon, vous devez utiliser les opérateurs de test de chaîne (= et !=).

L'autre petit problème est la cession. Vous l'avez comme $phone_missing=true, alors qu'il devrait être phone_missing=true (pas de signe dollar).

Désolé si c'est un peu dense, c'est parce que I am. Ça a été une longue journée. :)

5
Alexios

N'utilisez pas de chaîne pour booléen. Utilisez un entier.

Contribution:

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

Production:

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, l'état de retour est 0; sinon le statut de retour est 1. C'est exactement équivalent à laisser "expression".

4
Cyker

D'autres réponses vous ont donné une solution, mais je vais expliquer ce qui n'allait pas avec la pensée originale.

Les variables Bash n'ont pas de types, il n'y a donc pas de variable booléenne ou de valeur comme true ou false. Fondamentalement, toutes les variables bash ne sont que des chaînes.

Lorsque vous testez une variable/chaîne en bash sans spécifier le type de test (-n ou -z), il sera par défaut un -n (chaîne de longueur non nulle).

Donc [ "$var" ] est équivalent à [ -n "$var" ]. Aussi longtemps que $var contient au moins 1 caractère, il a été évalué comme vrai.

Puisque vous avez annulé l'expression, [ ! "$var" ] est équivalent à [ ! -n "$var" ]. Donc si $var contient au moins 1 caractère, puis l'expression est fausse.

Puisque false (qui n'est qu'une chaîne) contient 5 caractères, l'expression est fausse. Si la chaîne que vous définissez n'a pas d'importance phone_missing à (true, 0, 1), l'expression sera toujours false car est phone_missing est une longueur non nulle. La seule façon de le faire true est phone_missing="" car il s'agit d'une longueur nulle.

3
wisbucky

J'aurais fait cela comme un commentaire pour soutenir James Ko, mais je n'avais pas le représentant pour commenter ou voter publiquement.

Le problème ici est que les crochets [] sont une notation pour effectuer un test de comparaison tel que l'égalité de valeur ou de chaîne.

La véracité des chaînes en bash pour une chaîne vide est "" (chaîne vide) donne la valeur false (valeur de retour 1) et toute chaîne non vide "false" "true" ou "bob's your oncle" prend la valeur true (valeur de retour 0).

Vous pouvez le prouver avec:

    if [ "foo" ]; then 
      echo true is $?; 
    else 
      echo false is $?; 
    fi

Le $?above est une variable spéciale qui contient le dernier état de sortie des commandes (0 succès, et tout> 0 pour le code d'erreur) et affichera true is 0. Vous pouvez remplacer "foo" avec tout ce que vous voulez et le résultat sera le même sauf si vous le remplacez par une chaîne vide "" ou '' auquel cas il évaluera la condition else et affichera false is 1.

Lorsque vous utilisez les crochets [], l'instruction interne telle que! $ phone_missing est évalué puis retourne un 0 pour vrai ou 1 pour false à l'instruction if control. Étant donné que le crochet évalue les comparaisons de chaînes et de valeurs, $ phone_missing est d'abord étendu à la chaîne non vide "false" qui est évaluée par le [] comme une chaîne non vide (ou true), puis par le! inverse le résultat qui aboutit à l'instruction if obtenant un faux (ou retourne la valeur 1) et ignore le corps conditionnel exécutant l'instruction else s'il est présent.

Comme l'a dit James Ko, la notation serait de simplement passer la variable contenant votre "booléen" à l'instruction if control. Note qu'en bash un booléen true et false doit être en minuscules comme dans: bool_true = true bool_false = false Tout autre cas sera évalué en tant que chaînes et non booléen.

Donc, en utilisant votre exemple et la réponse de James Ko, ce serait:

phone_missing=false
echo "missing $phone_missing"

if ! $phone_missing 
then
  echo "Lost phone at $readabletime"
  $phone_missing=true
fi

ou comme je préfère pour la lisibilité (syntaxiquement le même et celui de James Ko)

phone_missing=false
echo "missing $phone_missing"

if ( ! $phone_missing ); then
  echo "Lost phone at $readabletime"
  $phone_missing=true
fi

D'autres réponses, comme par Alexios, vérifient littéralement le contenu de la chaîne. De sorte que l'un des éléments suivants entraîne un faux:

phone_missing=false
if [ "$phone_missing" != false ]; then
    echo "phone_missing is not 'false' (but may be non-true, too)"
fi
if [ "$phone_missing" == true ]; then
    echo "phone_missing is not 'false' (but may be non-true, too)"
fi
if [ "$phone_missing" == Bobs_your_uncle ]; then
    echo "phone_missing is not 'false' (but may be non-true, too)"
fi
1
DVS