1276 votes

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

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

variable=$false

variable=$true

Est-ce correct ? De même, si je voulais mettre à jour cette variable, utiliserais-je la même syntaxe ? Enfin, la syntaxe suivante pour utiliser des variables booléennes comme expressions est-elle correcte ?

if [ $variable ]

if [ !$variable ]

1526voto

miku Points 63392

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 que les commentaires avant la révision du 12 février 2014 ne se rapportent qu'à 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 concernant le bash builtin. true le 2 juin 2010 ne s'applique qu'à la réponse originale, pas à la réponse révisée.

41 votes

Pour expliquer ce qui se passe : le if exécute le contenu de la variable qui est la fonction intégrée de Bash. true . Toute commande peut être définie comme la valeur de la variable et sa valeur de sortie sera évaluée.

7 votes

@pms Les opérateurs "-o" et "-a" ne sont que pour la commande "test" (alias "[]"). Il s'agit plutôt de "if + commande", sans le "test". (Comme "if grep foo file ; then ...".) Donc, utilisez la commande normale && et || opérateurs : # t1=true; t2=true; f1=false; # if $t1 || $f1; then echo is_true ; else echo is_false; fi; (renvoie "vrai", puisque t1=vrai) # if $t1 && $f1 || $t2; then echo is_true ; else echo is_false; fi (renvoie "vrai", puisque t2=vrai) . Encore une fois, cela ne fonctionne QUE parce que "true"/"false" sont des bash-builtins (retournant true/false). Vous ne pouvez pas utiliser "if $var..." à moins que var soit une commande (c'est-à-dire vrai ou faux).

16 votes

-1, voir ma réponse pour une explication.

1027voto

Dennis Points 5020

TL;DR

my_bool=true

if [ "$my_bool" = true ]

Problèmes avec le système de Miku ( original ) répondre

Je le fais. pas recommande la réponse acceptée 1 . 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 suivants 2 cette condition sera évaluée à vrai et exécuter 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

En général, vous voulez que votre condition soit évaluée comme vraie uniquement lorsque votre variable "booléenne", var dans cet exemple, est explicitement défini comme vrai. Tous les autres cas sont dangereusement trompeurs !

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

Voici un exemple anodin :

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!

Il est plus prudent de citer vos variables, par ex. if "$var"; then . Dans les cas ci-dessus, vous devriez obtenir un avertissement indiquant que la commande n'est pas trouvée. Mais on peut encore faire mieux (voir mes recommandations en bas de page).

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

Questions relatives à 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!

On s'attendrait à ce que la condition ci-dessus soit évaluée à false, et donc à ce que l'instruction imbriquée ne soit jamais exécutée. Surprise !

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

Ce que je faire recommander :

Voici comment je vous recommande de vérifier vos "booléens". Ils fonctionnent comme prévu.

my_bool=true

if [ "$my_bool" = true ]; then
if [ "$my_bool" = "true" ]; then

if [[ "$my_bool" = true ]]; then
if [[ "$my_bool" = "true" ]]; then
if [[ "$my_bool" == true ]]; then
if [[ "$my_bool" == "true" ]]; then

if test "$my_bool" = true; then
if test "$my_bool" = "true"; then

Ils sont tous à peu près é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. Cette liste n'est pas exhaustive.
  3. Une commande valide dans ce contexte signifie une commande qui existe. Il importe peu que la commande soit utilisée correctement ou incorrectement. Par exemple man woman sera toujours considérée comme une commande valide, même si une telle page de manuel n'existe pas.
  4. Pour les commandes invalides (inexistantes), Bash se plaindra simplement que la commande n'a pas été trouvée.
  5. Si vous tenez à la longueur, la première recommandation est la plus courte.

12 votes

Utilisation de == avec [ ou test n'est pas portable. Considérant que la portabilité est le seul avantage [ / test a plus de [[ , s'en tenir à = .

2 votes

@Scott J'utilise poisson comme interpréteur de commandes principal, qui possède un langage de script sain comparé à bash, à mon avis.

0 votes

@Kranach c'est la sortie attendue et ce que je considère comme une commande valide même si la commande man est utilisée de manière incorrecte. Si elle était invalide, vous verriez man: command not found . C'est ce que je veux dire avec la note de bas de page n°3. (Edit : Posté avant de voir votre edit :))

194voto

Mike Holt Points 932

Il semble qu'il y ait un malentendu à propos de l'utilitaire Bash true et, plus précisément, 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 l'utilitaire Bash true ni /bin/true ni aucune autre saveur de la true commande. Dans ce cas, true n'est rien d'autre qu'une simple chaîne de caractères, et aucun appel à la fonction true n'est jamais faite, ni par l'affectation de variable, ni par l'évaluation de l'expression conditionnelle.

Le code suivant est fonctionnellement identique au code de 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 site uniquement 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 ". Et c'est tout. Il n'y a aucune tentative d'appeler une commande ou un module intégré nommé yeah et il n'y a pas non plus (dans l'exemple de miku) de traitement spécial lorsque Bash analyse le jeton true . C'est juste une chaîne, et une chaîne complètement arbitraire.

Mise à jour (2014-02-19) : Après avoir suivi le lien dans la réponse de miku, je vois maintenant d'où vient une partie de la confusion. La réponse de Miku utilise des parenthèses simples, mais l'extrait de code vers lequel il renvoie 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 se comporter de la même façon, mais les supports changent complètement ce qui se passe sous le capot.

Voici ce que fait Bash dans chaque cas :

Pas d'équerres :

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

Supports :

  1. Développez la variable $the_world_is_flat à la chaîne de caractères "true" .
  2. Analyser l'expression conditionnelle maintenant complètement développée, qui est de la forme string1 = string2 . Le site = est l'opérateur de bash comparaison de chaînes opérateur. Alors...
  3. Faire une comparaison de chaîne sur "true" et "true" .
  4. Oui, les deux chaînes étaient les mêmes, donc la valeur de la conditionnelle est vraie.
  5. Exécuter le if de la déclaration then clause.

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

Pour illustrer ce point, considérons les deux extraits de code suivants :

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

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

Ce code imprime juste "Bien essayé". 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. Le site == est un synonyme spécifique à Bash de = et, d'après ce que j'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 == les opérateurs de comparaison de chaînes de caractères utilisés dans l'un ou l'autre [ ] ou [[ ]] des tests. Je ne suggère pas que = et == sont interchangeables partout dans bash.

Par exemple, il est évident que vous ne pouvez pas faire d'assignation de variable avec == tels que var=="foo" (enfin, techniquement, vous peut le faire, mais la valeur de var sera "=foo" parce que Bash ne voit pas de == opérateur ici, il voit un = (affectation), suivi de la valeur littérale ="foo" qui devient juste "=foo" ).

En outre, bien que = et == sont interchangeables, vous devez garder à l'esprit que la manière dont ces tests fonctionnent fait dépendent du fait que vous l'utilisiez à l'intérieur [ ] ou [[ ]] et aussi sur le fait que les opérandes sont ou non cités. Vous pouvez en savoir plus à ce sujet dans Guide avancé du script Bash : 7.3 Autres opérateurs de comparaison (faites défiler vers le bas jusqu'à la discussion sur = et == ).

0 votes

L'approche sans parenthèses a également l'avantage de vous permettre d'écrire des phrases claires et nettes (je pense) en une seule ligne comme $the_world_is_flat && echo "you are in flatland!"

10 votes

C'est vrai. Bien que je ne plaide pas pour (ou contre) l'une ou l'autre approche. Je voulais juste clarifier certaines des informations erronées qui sont votées ici, afin que les personnes qui tombent sur ce sujet plus tard ne partent pas avec un tas d'idées fausses sur la façon dont tout cela fonctionne.

0 votes

Utilisation de == avec [ / test n'est pas portable ; si vous voulez utiliser == utiliser [[ . Si vous voulez utiliser [ utiliser = .

23voto

Hbar Points 187

Il y a longtemps, quand tout ce que nous avions était sh les booléens ont été traités en s'appuyant sur une convention de l'interface utilisateur de l'UE. test programme où test renvoie un statut de sortie faux s'il est exécuté sans aucun argument.

Cela permet de considérer qu'une variable non définie est fausse et qu'une variable définie par une valeur quelconque est vraie. Aujourd'hui, test est un module intégré à Bash et est communément connu sous son alias à un caractère. [ (ou un exécutable à utiliser dans les shells qui en sont dépourvus, 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 auteurs de script préfèrent utiliser la commande composée [[ qui imite test mais sa syntaxe est plus agréable : les variables comportant des espaces n'ont pas besoin d'être citées ; on peut utiliser la fonction && et || comme des opérateurs logiques avec une précédence bizarre, et il n'y a pas de limitations POSIX sur le nombre de termes.

Par exemple, pour déterminer si FLAG est activé et si 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 devenir confus lorsque des espaces, des chaînes de longueur nulle et des variables nulles sont nécessaires et aussi lorsque votre script doit fonctionner avec plusieurs shells.

3 votes

[ n'est pas seulement un alias dans bash . Cet alias existe également sous la forme d'un fichier binaire (ou d'un lien pointant vers) et peut être utilisé avec la commande bare sh . Vérifier ls -l /usr/bin/\[ . Avec bash / zsh vous devriez plutôt utiliser [[ qui est un véritable interne pur et est beaucoup plus puissant.

1 votes

@dolmen [ et test est également une COMMANDE BUILING SHELL de Bash selon la page de manuel de Bash, donc il ne devrait pas y avoir de problème de performance. Même chose avec par exemple Dash. (/bin/sh peut juste être un lien symbolique vers /bin/dash). Pour utiliser l'exécutable, vous devez utiliser le chemin complet, c'est-à-dire /usr/bin/\[ .

13voto

Pyrolistical Points 12457

Au lieu de simuler un booléen et de laisser un piège aux futurs lecteurs, pourquoi ne pas utiliser une meilleure valeur que true et false ?

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

0 votes

Pourquoi pas des entiers ?

6 votes

@Blauhirn car les entiers sont utilisés différemment selon les langues. Dans certaines langues 0 contraint à false et 1 à true . En ce qui concerne les codes de sortie de programme (que bash utilise historiquement), il s'agit de 0 pour un résultat positif ou true et tout le reste est négatif/erreur ou false .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X