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

981

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? De plus, 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 ]
hassaanm
la source
73
IL FAUT SE MÉFIER! trueet falsedans le contexte de la plupart des extraits ci-dessous, ce ne sont que des chaînes simples, pas le bash built-ins!!! Veuillez lire la réponse de Mike Holt ci-dessous. (Ceci est un exemple où une réponse fortement votée et acceptée est à
mon humble avis
7
@mjv La plupart de la confusion sur cette question (et la réponse de Miku) était due au fait que Miku a révisé sa réponse à un moment donné après que plusieurs commentaires ont été publiés décrivant comment la réponse de Miku impliquait d'appeler le bash intégré true. Il s'avère que la réponse originale de Miku appelait vraiment la fonction trueintégrée, mais pas la réponse révisée. Cela a fait que ces commentaires semblaient faux sur le fonctionnement du code de Miku. La réponse de Miku a depuis été modifiée pour afficher explicitement le code original et le code révisé. Espérons que cela mettra définitivement un terme à la confusion.
Mike Holt
2
[ true ] && echo yes, true is trueet (upppsss) [ false ] && echo yes, false is also true. / bin / true et / bin / false donne un code retour $? pour les fonctions et non pour la comparaison.
fcm
Si var est défini, variable=somethingil est vrai, si non défini , variable=ce serait faux [[ $variable ]] && echo true || echo falseet inversé[[ ! $variable ]] && echo false || echo true
Ivan

Réponses:

1203

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 d'origine est incluse ici est que les commentaires avant la révision du 12 février 2014 ne concernent que la réponse d'origine et que de nombreux 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 truele 2 juin 2010 ne s'applique qu'à la réponse d'origine, pas à la version révisée.

miku
la source
37
Pour expliquer ce qui se passe: l' ifinstruction exécute le contenu de la variable qui est le Bash intégré true. Toute commande pourrait être définie comme la valeur de la variable et sa valeur de sortie serait évaluée.
pause jusqu'à nouvel ordre.
7
@pms Les opérateurs "-o" et "-a" sont uniquement pour la commande "test" (alias "[]"). Au lieu de cela, il s'agit de "if + command", sans le "test". (Comme "if grep foo file; then ...".) Donc, utilisez les opérateurs normal &&et ||: # t1=true; t2=true; f1=false;# if $t1 || $f1; then echo is_true ; else echo is_false; fi; (retourne "true", puisque t1 = true) # if $t1 && $f1 || $t2; then echo is_true ; else echo is_false; fi (retourne "true", puisque t2 = true) . Encore une fois, cela fonctionne UNIQUEMENT parce que "true" / "false" sont des commandes intégrées à bash (renvoyant true / false). Vous ne pouvez pas utiliser "if $ var ..." à moins que var soit un cmd (c'est-à-dire vrai ou faux)
michael
14
-1, voir ma réponse pour une explication.
Dennis
3
Beaucoup d'informations incorrectes, ici. / bin / true n'est pas utilisé efficacement. Voir la réponse de Dennis.
ajk
1
Ce code n'est pas le même et ne fonctionne pas de la même manière que l'article lié. Le code lié appelle un programme par le nom stocké dans une variable mais le code dans cette réponse n'est qu'une comparaison de chaînes.
Questions sur Quolonel du
795

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 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 à true et exécutera 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 règle générale, vous souhaitez que votre condition soit évaluée à true lorsque votre variable "booléenne", vardans cet exemple, est explicitement définie sur true. 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 est évaluée à vrai pour les commandes valides 3, 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!

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

Voir également 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 attendriez à ce que la condition ci-dessus soit évaluée à false, n'exécutant ainsi jamais l'instruction imbriquée. Surprise!

La citation de la valeur ( "false"), la citation de la variable ( "$var"), ou l'utilisation de testou à la [[place de [, ne font aucune différence.

Ce que je ne recommande:

Voici les moyens 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é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 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 womanserait toujours considérée comme une commande valide, même si aucune page de manuel de ce type 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.
Dennis
la source
8
L'utilisation ==avec [ou testn'est pas portable. Considérant que la portabilité est le seul avantage [/ testa plus [[, rester avec =.
chepner
2
@Scott J'utilise fish comme mon shell principal, qui a un langage de script sain par rapport à bash à mon avis.
Dennis
1
Ouais, je ne pouvais tout simplement pas trouver dans les commentaires une appréciation pour cette blague cachée, alors je devais le signaler =)
Kranach
5
Pour moi, conceptuellement, il est plus facile de comprendre si j'utilise bool = "true". Ensuite, il est clair qu'il ne s'agit que d'une chaîne et non d'une valeur spéciale ou intégrée.
wisbucky
1
@dolmen absolument, l'évaluation des entrées n'est pas aussi risquée lorsque vous contrôlez les entrées, mais je considère toujours que c'est une mauvaise pratique qui devrait être évitée si elle peut facilement être évitée. Quelqu'un qui n'a jamais vu et utilisé que l'ancien style peut ne pas connaître ses défauts qui peuvent provoquer un comportement inattendu.
Dennis
175

Il semble y avoir un malentendu ici à propos de la fonction intégrée de Bash true, et plus précisément de la façon 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 Bash intégré true, ni /bin/true, ni aucune autre saveur de la truecommande. Dans ce cas, truen'est rien de plus qu'une simple chaîne de caractères, et aucun appel à la truecommande / builtin n'est jamais effectué, ni par l'affectation 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

La seule 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 ça. Il n'y a aucune tentative d'appeler une commande ou une commande intégrée nommée yeah, ni (dans l'exemple de miku) aucune sorte de traitement spécial en cours lorsque Bash analyse le jeton true. C'est juste une chaîne, et complètement arbitraire.

Mise à jour (2014-02-19): Après avoir suivi le lien dans la réponse de miku, maintenant je vois d'où vient une partie de la confusion. La réponse de Miku utilise des crochets simples, mais l'extrait de code auquel il renvoie 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_flatdans la chaîne "true".
  2. Essayez d'analyser la chaîne en "true"tant que commande.
  3. Recherchez et exécutez la truecommande (soit intégrée, soit /bin/true, selon la version de Bash).
  4. Comparez le code de sortie de la truecommande (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 tout le reste indique l'échec.
  5. Puisque le code de sortie était 0 (succès), exécutez la clause de l' ifinstructionthen

Supports:

  1. Développez la variable $the_world_is_flatdans la chaîne "true".
  2. Analyser l'expression conditionnelle désormais entièrement développée, qui est de la forme string1 = string2. L' =opérateur est l' opérateur de comparaison de chaînes de bash . Donc...
  3. Faites une comparaison de chaînes 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 de l' ifinstruction then.

Le code sans parenthèses fonctionne, car la truecommande renvoie un code de sortie de 0, ce qui indique la réussite. Le code entre crochets fonctionne, car la valeur de $the_world_is_flatest identique à la chaîne littérale truesur le côté droit de la =.

Juste pour enfoncer le clou, 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 simplement "Nice try." La commande de redémarrage 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 à Bash =et, pour autant que je sache, ils fonctionnent exactement de la même manière dans tous les contextes.

Notez, cependant, que je parle spécifiquement des opérateurs de comparaison de chaînes =et ==utilisés dans l'un [ ]ou dans les [[ ]]tests. Je ne suggère pas cela =et ==sont interchangeables partout dans bash.

Par exemple, vous ne pouvez évidemment pas faire d'affectation de variable avec ==, comme var=="foo"(eh bien techniquement, vous pouvez le faire, mais la valeur de varsera "=foo", parce que Bash ne voit pas d' ==opérateur ici, il voit un =opérateur (d'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 ces travaux de la façon dont les tests ne dépendent que vous utilisez à l'intérieur [ ]ou [[ ]], et aussi si les opérandes sont cités 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 sur =et ==).

Mike Holt
la source
L'approche sans parenthèse a également l'avantage de vous permettre d'écrire des lignes $the_world_is_flat && echo "you are in flatland!"
simples
9
Vrai. Cependant, je ne préconise pas (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 gens qui tombent sur ce sujet plus tard ne repartent pas avec un tas d'idées fausses sur la façon dont tout cela fonctionne.
Mike Holt
1
La raison de la confusion est que la réponse originale de miku était de 4 ans. Toutes les références au builtin trueont été faites concernant la réponse originale. (La réponse révisée du 12 février 2014 n'a pas été soumise par miku.) J'ai modifié la réponse pour inclure à la fois l'original et la version révisée. Ensuite, les commentaires des gens ont un sens.
wisbucky
1
En lisant les réponses proposées ici, j'ai l'impression qu'il n'y a rien de tel que d'utiliser réellement le réel true. Y a-t-il un moyen? Je soupçonne que de nombreux programmeurs habitués à des langues plus strictes qui consultent cette réponse pour les aider à mélanger un peu de bashcolle pour leur faciliter la vie auraient besoin d'un ===opérateur pour que les chaînes et les "booléens" ne soient pas réellement interchangeables. Faut - il juste coller à 0 et 1 et de l' utilisation (( $maybeIAmTrue ))comme suggéré dans Quolonel Question de réponse ?
SeldomNeedy
2
Pour répondre au commentaire de SeldomNeedy, oui, vous pouvez utiliser le réel true, mais généralement pas comme quelque chose pour comparer une variable, car le réel truen'a aucune valeur en soi. Il ne fait que définir l'état de sortie sur 0, indiquant le succès. Il convient de noter qu'elle est essentiellement équivalente à la soi-disant "commande nulle", ou :. En ce qui concerne l'utilisation de 0et 1, c'est ce que je fais dans tous mes scripts de nos jours où j'ai besoin de booléens. Et j'utilise l' (( ))opérateur au lieu de [[ ]]pour évaluer. Donc, par exemple, si je l'ai flag=0, je peux le faireif (( flag )); then ...
Mike Holt
57

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

Production:

vrai
pas faux

Questions sur Quolonel
la source
3
avantages: (1.) le comportement est similaire à la façon de gérer les bools de C, (2.) la syntaxe est très concise / minimale (ne nécessite pas de variable de droite et des opérateurs comme '=' ou '=='), (3 .) <subjective> pour moi, je comprends ce qui se passe sans une longue explication ... contraste avec les réponses de Miku et Dennis qui semblent toutes deux exiger des explications longues </subjective>
Trevor Boyd Smith
3
@TrevorBoydSmith Pourquoi ne venez-vous pas de dire "pour: tout, contre: rien". Économiserait les coûts d'amortissement sur votre clavier et votre moniteur à long terme.
Questions Quolonel
4
Pour une utilisation interactive, comme les lignes simples, assurez-vous de laisser un espace après !, ou cela fera une expansion de l'historique. ((! foo))fonctionne, il en va de même ! ((foo)). J'adore cette solution, BTW. Enfin une manière concise de faire des variables booléennes. ((foo || bar))fonctionne comme prévu.
Peter Cordes
5
(())développe les variables récursivement, ce à quoi je ne m'attendais pas. foo=bar; bar=baz; ((foo)) && echo echon'imprime rien, mais c'est vrai avec baz=1. Ainsi, vous pouvez soutenir foo=trueet foo=falseainsi que 0 ou 1 en faisant true=1.
Peter Cordes
2
@quolonel merci pour la ressource très utile. Bien sûr, ma compréhension est limitée - c'est la nature humaine d'être limitée dans toute compréhension quel que soit le domaine. Cependant, pourriez-vous me dire laquelle de mes déclarations vous amène à supposer que ma compréhension de cette question particulière est incomplète?
Hubert Grzeskowiak
35

Longue histoire courte:

Il n'y a pas de booléens dans Bash

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

Partout où vous voyez trueou falsedans Bash, c'est soit une chaîne, soit une commande / intégrée 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 lorsque la commande retourne le code de sortie 0. trueet falsesont builtins Bash et parfois aussi des programmes autonomes qui ne font que retourner le code de sortie correspondant.

Le conditionnel ci-dessus équivaut à:

COMMAND && ...

Lorsque vous utilisez des crochets ou la testcommande, vous comptez sur le code de sortie de cette construction. Gardez à l' esprit que [ ]et [[ ]]sont également à quelques commandes / builtins comme les autres. Donc ...

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

Correspond à

if COMMAND; then echo yes; fi

et COMMANDvoici[[ 1 == 1 ]]

La if..then..ficonstruction est juste du sucre syntaxique. Vous pouvez toujours simplement exécuter les commandes séparées par une double esperluette pour le même effet:

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

Lorsque vous utilisez trueet falsedans ces constructions de test, vous ne passez en fait 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; comparer toujours avec des chaînes ou des nombres

Pour que cela soit clair pour les futurs lecteurs, je recommanderais toujours d'utiliser des guillemets autour trueet 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. It can be used for strict checking of dangerous operations. This condition is false for anything but the literal string "true".
Hubert Grzeskowiak
la source
Je préfère utiliser Tet Fpréciser que ce ne sont pas de vraies valeurs booléennes.
phk
1
Je ne suis pas d'accord avec "toujours utiliser des crochets doubles dans bash". En fait, dans presque tous les scripts que j'ai écrits, j'utilise des crochets simples, sauf lorsque je dois faire une correspondance de modèle. Je pense qu'il faut comprendre la différence entre [(ie test) [[et utiliser celle qui convient à son besoin.
Weijun Zhou
@WeijunZhou vous demande dans quels cas les crochets simples sont meilleurs?
Hubert Grzeskowiak
C'est plus un goût personnel, je trouve juste que c'est trop audacieux pour dire "Utilisez toujours des crochets doubles dans bash". Mais il y a quelques cas de bord que j'ai utilisés. Les crochets simples vous permettent de spécifier le test lui-même dans une var. À titre d'exemple simplifié, considéronsif ....; then mytest='-gt'; else mytest='-eq'; fi; #several lines of code; if [ "$var1" "$mytest" "$var2" ]; then ...; fi
Weijun Zhou
1
@WeijunZhou Votre exemple est un argument fort contre les crochets simples. Cela rend le code beaucoup plus difficile à comprendre et ouvre la fenêtre grande ouverte aux erreurs. Les crochets doubles sont plus stricts et encouragent un code plus propre.
Hubert Grzeskowiak
18

Il y a longtemps, alors que tout ce que nous avions était sh, les booléens étaient gérés en s'appuyant sur une convention du testprogramme qui testrenvoie un faux état de sortie s'il était exécuté sans aucun argument.

Cela permet de penser à une variable qui n'est pas définie comme fausse et à une variable définie comme vraie. Aujourd'hui, testest une fonction intégrée à Bash et est généralement connue par son alias à un caractère [(ou un exécutable à utiliser dans les coquilles qui en manquent, comme le note le 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 rédacteurs de scripts 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 ||comme opérateurs logiques avec une priorité 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 truc peut être déroutant lorsque des espaces, des chaînes de longueur nulle et des variables nulles sont tous nécessaires et également lorsque votre script doit fonctionner avec plusieurs shells.

Hbar
la source
3
[n'est pas seulement un alias à l'intérieur bash. Cet alias existe également en tant que fichier binaire (ou en tant que lien pointant vers) et peut être utilisé avec le bare sh. Vérifiez ls -l /usr/bin/\[. Avec bash/, zshvous devriez utiliser à la place [[un véritable interne pur et beaucoup plus puissant.
dolmen
1
@dolmen [et testest également une commande Bash SHELL BUILTIN selon la page de manuel 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/\[.
jarno
12

Comment puis-je 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]

La réponse est donc assez claire. Il n'y a pas de variable booléenne dans Bash.

Toutefois:

En utilisant une instruction declare, nous pouvons limiter l'attribution de valeur aux 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"

L' roption in declareet readonlyest utilisée pour indiquer explicitement que les variables sont en lecture seule . J'espère que le but est clair.

sjsam
la source
1
Pourquoi tu ne fais pas ça declare -ir false=0 true=1? Quel est l'avantage d'utiliser un tableau?
Benjamin W.
@BenjaminW. Je voulais juste mentionner l' roption et la readonlycommande. Je le ferais comme vous l'avez suggéré dans mes scripts
sjsam
peut-être que j'ai raté quelque chose, mais pourquoi ne pas vrai et faux déclaré de cette façon utiliser le signe dollar? $ true $ false
qodeninja
Copier littéralement ma réponse et l'aggraver.
Quolonel Questions
@QuolonelQuestions Les variables Bash ne sont pas typées , il est donc inutile de dire declare and use boolean variables. Nous pourrions simplement, à plus d'un titre, imiter / supposer qu'une variable a un type . Je n'ai vu cela mentionné nulle part dans votre réponse.
sjsam
10

Au lieu de simuler un booléen et de laisser un piège aux 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
Pyrolistique
la source
pourquoi pas des entiers?
phil294
3
@Blauhirn car les entiers sont utilisés différemment selon les langues. Dans certaines langues , 0contraint à falseet 1à true. En ce qui concerne les codes de sortie du programme (que bash utilise historiquement), c'est 0pour un résultat positif ou trueet tout le reste est négatif / erreur ou false.
Hubert Grzeskowiak
7

POSIX (Interface du système d'exploitation portable)

Je 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 qu'elles sont trop spécifiques à Bash .

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


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

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

    Remarque importante: je ne recommande plus cela, car il est lentement obsolète et plus difficile de combiner plusieurs instructions.

  2. Imaginez ce que ces mots trueet falsesignifient pour la coquille, testez-le vous-même:

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

    Mais en utilisant des guillemets:

    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 avez une idée de la qualité de l'utilisation d'un mot clé approprié sans guillemets .

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

  3. Qu'est-ce que ça veut dire? Eh bien, plusieurs choses.

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

    • Puisqu'ils sont traités comme des nombres, vous devez également les traiter comme ça, c'est-à-dire si vous définissez une variable, dites:

      var_bool=true
      echo "$var_bool"
       true

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

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

    Comme vous pouvez le constater par vous-même, le shell imprime une truechaîne pour la première fois que vous l'utilisez, mais depuis lors, tout fonctionne via un nombre 0représentant trueou 1représentant false, respectivement.


Enfin, ce que vous devez faire avec toutes ces informations

  • Tout d'abord, une bonne habitude serait d'assigner 0au lieu de true; 1au lieu de false.

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

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

Concernant la syntaxe, il s'agit d'une méthodologie simple que j'utilise (par exemple) pour gérer de manière cohérente et saine la logique booléenne:

# 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 n'est jamais déclarée, la réponse est: # false

Ainsi, une façon simple de définir une variable à true ( en utilisant cette méthode de syntaxe) serait, var=1; à l' inverse, var=''.

Référence:

-n = Vrai si la longueur de la chaîne var n'est pas nulle.

-z = Vrai si la longueur de la chaîne var est nulle.

Eric P
la source
5

Dans de nombreux langages de programmation, le type booléen est, ou est implémenté comme, un sous-type d'entier, où truese comporte comme 1et falsese comporte comme 0:

Mathématiquement , l'algèbre booléenne ressemble au module arithmétique entier 2. Par conséquent, si un langage ne fournit pas de type booléen natif, la solution la plus naturelle et la plus efficace consiste à utiliser des entiers. Cela fonctionne avec presque toutes les langues. Par exemple, dans Bash, vous pouvez faire:

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

homme bash :

((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".

Cyker
la source
5

Bill Parker est rejeté , car 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 différent de zéro. 1 fonctionnera pour false, tout comme 9999 et -1. La même chose avec les valeurs de retour de fonction - 0 est un succès et tout ce qui n'est pas nul 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 des crochets doubles 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, -eqest 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, 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 crochets autour, il n'émettra pas de déclaration d'erreur, mais il donnera une valeur erronée (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 que la valeur intégrée soit une 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 une sortie erronée. Fondamentalement, tout (autre que la condition d'erreur répertoriée ci-dessus) qui définit une variable à une valeur numérique et la compare à une véritable / fausse prédéfinie, ou définit une variable à une vraie / fausse prédéfinie et la compare à une valeur numérique. En outre, tout ce qui définit une variable sur un true / false builtin et fait une comparaison en utilisant -eq. Évitez donc -eqles 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 des valeurs internes vraies / fausses pour votre comparaison et vos évaluations (comme l'a noté Mike Hunt, ne les mettez pas entre guillemets). Utilisez ensuite un signe égal ou simple ou double (= ou ==) et des parenthèses 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 doubles simplement parce que j'aime 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à.

Randyman99
la source
2
Les éléments intégrés true/ falsene sont pas utilisés ici (ignorez ce que la mise en évidence de la syntaxe de certains éditeurs pourrait impliquer), en particulier dans les […]cas où vous pouvez le considérer comme une simple chaîne ici (celle qui est donnée comme paramètre à la [commande).
phk
Vous l'avez maintenant.
Peter Mortensen
4

Mes conclusions et suggestions diffèrent un peu des autres articles. J'ai trouvé que je pouvais utiliser des "booléens" essentiellement comme on le ferait dans n'importe quelle langue "régulière", sans le "saut de cerceau" suggéré ...

Il n'y a pas besoin de []comparaisons de chaînes explicites ... J'ai essayé plusieurs distributions Linux. J'ai testé Bash, Dash et BusyBox . Les résultats étaient toujours les mêmes. Je ne sais pas de quoi parlent les premiers messages votés. Peut-être que les temps ont changé et que c'est tout?

Si vous définissez une variable sur true, elle est ensuite évaluée comme "affirmative" dans un conditionnel. Définissez-le sur false, et il est évalué à "négatif". Très simple! La seule mise en garde est qu'une variable non définie est également évaluée comme vraie ! Ce serait bien s'il faisait le contraire (comme ce serait le cas dans la plupart des langues), mais c'est l'astuce - il vous suffit d'initialiser explicitement vos booléens sur vrai ou faux .

Pourquoi ça marche comme ça? Cette réponse est double. A) vrai / faux dans un shell signifie vraiment «pas d'erreur» vs «erreur» (c'est-à-dire 0 vs toute autre chose). B) true / false ne sont pas des valeurs - mais plutôt des instructions dans les scripts shell! En ce qui concerne le deuxième point, l'exécution trueou falsesur une ligne par elle-même définit la valeur de retour pour le bloc dans lequel vous vous trouvez, c'est false-à- dire une déclaration d '"erreur rencontrée", où vrai "efface" cela. L'utiliser avec une affectation à une variable "renvoie" cela dans la variable. Une variable non définie est évaluée comme truedans un conditionnel car cela représente également 0 ou "aucune erreur rencontrée".

Voir l'exemple de lignes Bash et les résultats ci-dessous. Testez-le vous-même si vous souhaitez confirmer ...

#!/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;

Rendements

when set to
it evaluates to true
when set to true
it evaluates to true
when set to false
it evaluates to false
BuvinJ
la source
1

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

Voici une implémentation d'un shorthanded 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"
llundin
la source
Oui, la décomposition fonctionnelle est sous-estimée.
Peter Mortensen
1

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 que tout le monde soit content, j'ai testé:

snapshotEvents=false

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

Ce qui a également bien fonctionné.

Le $snapshotEventsévalue le contenu de la valeur de la variable. Vous avez donc besoin de $.

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

volonté
la source
2
Lorsque vous supprimez les parenthèses, c'est exactement la réponse originale de @ miku en haut.
dolmen
1
Sans parenthèses, l'expression n'est pas évaluée.
le
@ le fera-t-il. Vous n'avez pas besoin du () s.
phil294
1
@Blauhirn ... Salut, j'ai basé mes commentaires sur des expériences avec GNU Bash sur un PC Linux Mint / Ubuntu. Vous avez probablement raison en théorie - il () n'est pas nécessaire. Ma seule réponse est de l'essayer, cela semble dépendre de la version de Bash, de l'expression ou du contexte réel et autres.
le
1

Voici une amélioration de la réponse originale de miku qui répond aux préoccupations de Dennis Williamson concernant le cas 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

À propos d'autres cas avec un contenu désagréable dans la variable, il s'agit d'un problème avec toute entrée externe alimentant un programme.

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

Il n'a pas à avoir un impact sur les performances du programme en le faisant à chaque utilisation de la variable comme le suggère Dennis Williamson .

dolmen
la source
1

Vous pouvez utiliser shFlags .

Il vous donne la possibilité de définir: DEFINE_bool

Exemple:

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

Depuis la ligne de commande, vous pouvez définir:

sh script.sh --bigmenu
sh script.sh --nobigmenu # False
gogasca
la source
GFlags n'a aucun sens dans cette réponse - c'est une bibliothèque C ++. Il ne peut pas être utilisé directement dans les scripts shell.
Jonathan Cross
Réponse mise à jour à shFlags qui est un port de GFlags à shell.
gogasca
0

Il s'agit d'un test de vitesse sur différentes façons de tester les valeurs "booléennes" dans 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

Il imprimerait quelque chose comme

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
la source
-2

Alternative - utilisez une fonction

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

La définition de la fonction est moins intuitive, mais la vérification de sa valeur de retour est très simple.

johnraff
la source
1
Ce n'est pas une variable que vous pouvez tester, mais une fonction constante
jarno
@jarno Le test de la valeur de retour d'une fonction est-il différent du test d'une variable, aux fins d'un script?
johnraff
Eh bien, la question concerne les variables.
jarno
Vrai, bien que l'utilisation dans un script shell soit la même.
johnraff
-2

Bash brouille vraiment la question avec des gens [, [[, ((, $((, etc.

Tout en marchant sur les espaces de code des autres. Je suppose que c'est surtout historique, où Bash a dû faire semblant d'être de shtemps en temps.

La plupart du temps, je peux simplement choisir une méthode et m'en tenir à elle. 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 testvardoit être réglé sur $TRUEou $FALSEà tout moment.

  2. Dans ((... les ))comparateurs, vous n'avez pas besoin des précédents $, ce qui les rend plus lisibles.

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

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

    testvar=$TRUE

    ce qui n'est pas si joli.

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

Bill Parker
la source
2
Vous devez déclarer vos constantes en lecture seule. Veuillez également toujours utiliser des accolades lorsque vous utilisez des variables. C'est une convention que tout le monde devrait respecter à mon humble avis. Le gros inconvénient de cette solution est que vous ne pouvez pas mélanger l'expression algébrique avec des indicateurs de test ou des comparaisons de chaînes.
Hubert Grzeskowiak