bash - template - putty window title command line




Wie deklarieren und verwenden Sie boolesche Variablen im Shell-Skript? (10)

Wie deklariere und verwende boolesche Variablen im Shell-Skript?

Im Gegensatz zu vielen anderen Programmiersprachen trennt Bash seine Variablen nicht durch "type". [1]

Die Antwort ist ziemlich klar. Es gibt keine boolean variable in bash. Jedoch :

Mit einer declare-Anweisung können wir die Bewertung auf Variablen beschränken. [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"

Mit der Option r in declare und readonly wird explizit angegeben, dass die Variablen schreibgeschützt sind . Hoffe, der Zweck ist klar.

Ich habe versucht, eine boolesche Variable in einem Shell-Skript mit der folgenden Syntax zu deklarieren:

variable=$false

variable=$true

Ist das richtig? Wenn ich diese Variable aktualisieren möchte, würde ich auch die gleiche Syntax verwenden? Schließlich ist die folgende Syntax für die Verwendung boolescher Variablen als Ausdrücke richtig:

if [ $variable ]

if [ !$variable ]

POSIX (Portable Betriebssystemschnittstelle)

Ich vermisse hier den Schlüsselpunkt, nämlich die Portabilität. Deshalb hat mein Header POSIX in sich.

Im Wesentlichen sind alle der BASH Antworten korrekt, mit der Ausnahme, dass sie zu sehr BASH sind.

Im Grunde möchte ich nur weitere Informationen zur Portabilität hinzufügen.

  1. [ und ] Klammern wie in [ "$var" = true ] sind nicht notwendig, und Sie können sie weglassen und den test Befehl direkt verwenden:

    test "$var" = true && CodeIfTrue || CodeIfFalse
    
  2. Stellen Sie sich vor, was diese Wörter true und false für die Shell bedeuten, testen Sie es selbst:

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

    Aber mit Zitaten:

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

    Das gleiche gilt für:

    echo $(("false"))
    

    Die Shell kann sie nur als Zeichenfolge interpretieren. Ich hoffe, Sie bekommen die Idee, wie gut es ist, ein richtiges Keyword ohne Anführungszeichen zu verwenden.

    Aber niemand hat es in früheren Antworten gesagt.

  3. Was das bedeutet? Nun, mehrere Dinge.

    • Sie sollten sich daran gewöhnen, dass die booleschen Schlüsselwörter tatsächlich wie Zahlen behandelt werden, das heißt true = 0 und false = 1 , denken Sie daran, dass alle Werte ungleich Null wie false behandelt werden.

    • Da sie wie Zahlen behandelt werden, sollten Sie sie auch so behandeln, dh wenn Sie Variable definieren, sagen Sie:

      var_a=true
      echo "$var_a"
      
       true
      

      Sie können einen entgegengesetzten Wert davon mit erstellen:

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

      Wie Sie selbst sehen können, druckt Shell zum ersten Mal eine true Zeichenkette, aber seither funktioniert alles über die Nummer 0 bzw. 1 .

Schließlich, was Sie mit all diesen Informationen tun sollten

  • Die erste gute Gewohnheit würde 0 statt true zuweisen; 1 statt false .

  • Die zweite gute Angewohnheit wäre, zu testen, ob die Variable gleich null ist:

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

Anstatt einen Booleschen Fehler zu fälschen und eine Falle für zukünftige Leser zu hinterlassen, warum nicht einfach einen besseren Wert als true und false verwenden?

Beispielsweise:

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

BASH verwechselt das Problem wirklich mit dem Beispiel [ , [[ , (( , $(( usw.)

Alle behandeln die Coderäume der anderen. Ich denke, das ist hauptsächlich historisch, wo bash so tun musste, als sei er gelegentlich.

Die meiste Zeit kann ich einfach eine Methode auswählen und dabei bleiben. In diesem Fall neige ich dazu, zu deklarieren (vorzugsweise in einer gemeinsamen Bibliotheksdatei, die ich in mein (n) tatsächliche (n) Skript (en) einschließen kann).

TRUE=1; FALSE=0

Ich kann dann den (( ... )) arithmetischen Operator verwenden, um so zu testen.

testvar=$FALSE

if [[ -d ${does_directory_exist} ]]
then
    testvar=$TRUE;
fi

if (( testvar == TRUE )); then
    # do stuff because the directory does exist
fi
  1. Sie müssen diszipliniert sein, Ihre testvar muss entweder auf $TRUE oder $FALSE sein.

  2. In (( ... )) Komparatoren brauchen Sie das vorangehende $ , was es lesbarer macht.

  3. Ich kann (( ... )) weil $TRUE=1 und $FALSE=0 , also numerische Werte.

  4. Der Nachteil ist, gelegentlich ein $ :

    testvar=$TRUE
    

    Das ist nicht so schön.

Es ist keine perfekte Lösung, aber es deckt jeden Fall ab, ich brauche einen solchen Test.


Es scheint hier einige Missverständnisse bezüglich der in true eingebauten Bash zu geben und insbesondere darüber, wie Bash Ausdrücke in Klammern erweitert und interpretiert.

Der Code in Mikus Antwort hat absolut nichts mit der Bash zu tun, die in true , noch /bin/true , oder irgendeinem anderen Geschmack des true Befehls eingebaut ist. In diesem Fall ist true nichts anderes als eine einfache Zeichenkette, und es wird niemals ein Aufruf an den true Befehl / Built-In vorgenommen, weder durch die Variablenzuweisung noch durch die Auswertung des Bedingungsausdrucks.

Der folgende Code ist funktional identisch mit dem Code in der Antwort der Miku:

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

Der einzige Unterschied besteht darin, dass die vier verglichenen Zeichen "y", "e", "a" und "h" anstelle von "t", "r", "u" und "e" sind. Das ist es. Es wird kein Versuch unternommen, einen Befehl oder Built-in namens yeah aufzurufen, noch gibt es (im Beispiel von miku) irgendeine Art von spezieller Behandlung, die stattfindet, wenn bash das Token true interpretiert. Es ist nur eine Zeichenfolge, und eine völlig willkürliche dabei.

Update (2/19/2014): Nachdem ich dem Link in Mikus Antwort gefolgt bin, sehe ich jetzt, wo etwas von der Verwirrung herkommt. Mikus Antwort verwendet einzelne Klammern, aber das Code-Snippet, auf das er verweist, verwendet keine Klammern. Es ist nur:

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

Beide Code-Snippets verhalten sich genauso, aber die Klammern ändern komplett, was unter der Haube passiert.

Hier ist was Bash in jedem Fall tut:

Keine Klammern:

  1. Erweitern Sie die Variable $the_world_is_flat auf die Zeichenfolge "true" .
  2. Versuchen Sie, die Zeichenfolge "true" als Befehl zu analysieren.
  3. Suchen Sie und führen Sie den true Befehl aus (entweder ein Built-In oder /bin/true , abhängig von der Bash-Version).
  4. Vergleichen Sie den Exit-Code des true Befehls (der immer 0 ist) mit 0. Erinnern Sie sich daran, dass in den meisten Shells ein Exit-Code von 0 den Erfolg anzeigt und alles andere einen Fehler anzeigt.
  5. Da der Exit-Code 0 (Erfolg) war, führe die then Klausel der if Anweisung aus

Klammern:

  1. Erweitern Sie die Variable $the_world_is_flat auf die Zeichenfolge "true" .
  2. Analysieren Sie den jetzt vollständig expandierten bedingten Ausdruck in der Form string1 = string2 . Der Operator = ist der String-Vergleichsoperator von bash. Damit...
  3. Machen Sie einen String Vergleich auf "true" und "true" .
  4. Ja, die zwei Saiten waren gleich, also ist der Wert der Bedingung wahr.
  5. Führen Sie die then Klausel der if Anweisung aus.

Der Code ohne Klammern funktioniert, weil der Befehl true Exit-Code 0 zurückgibt, der den Erfolg anzeigt. Der in Klammern $the_world_is_flat Code funktioniert, weil der Wert von $the_world_is_flat identisch mit dem String-Literal true auf der rechten Seite von = .

Um den Punkt nach Hause zu bringen, bedenken Sie die folgenden zwei Codeschnipsel:

Dieser Code (wenn er mit Root-Rechten ausgeführt wird) startet Ihren Computer neu:

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

Dieser Code druckt nur "Nice try". Der Neustartbefehl wird nicht aufgerufen.

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

Update (14.04.2014) Zur Beantwortung der Frage in den Kommentaren zum Unterschied zwischen = und == : AFAIK gibt es keinen Unterschied. Der == Operator ist ein Bash-spezifisches Synonym für = , und soweit ich gesehen habe, funktionieren sie in allen Kontexten genau gleich. Beachten Sie jedoch, dass ich speziell über die = und == String-Vergleichsoperatoren spreche, die in den Tests [ ] oder [[ ]] werden. Ich behaupte nicht, dass = und == überall in der Bash austauschbar sind . Zum Beispiel können Sie offensichtlich keine Variablenzuweisung mit == , wie zB var=="foo" ( var=="foo" technisch können Sie das tun, aber der Wert von var wird "=foo" , weil bash keins sieht == Operator hier, es wird ein Operator = (Zuweisung) angezeigt, gefolgt von dem Literalwert ="foo" , der gerade zu "=foo" .

Auch wenn = und == austauschbar sind, sollten Sie daran denken, dass die Funktionsweise dieser Tests davon abhängt, ob Sie sie in [ ] oder [[ ]] und ob die Operanden in Anführungszeichen stehen. Mehr dazu finden Sie hier: Advanced Bash Scripting Guide: 7.3 Andere Vergleichsoperatoren (scrollen Sie nach unten zur Diskussion von = und == ).


Hier ist ein einfaches Beispiel, das für mich funktioniert:

temp1=true
temp2=false

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

Hier ist eine Verbesserung der ursprünglichen Antwort von Miku, die Dennis Williamson 's Bedenken bezüglich des Falles adressiert, wo die Variable nicht gesetzt ist:

the_world_is_flat=true

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

Und um zu testen, ob die Variable false :

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

Über andere Fälle mit einem unangenehmen Inhalt in der Variablen ist dies ein Problem mit irgendeiner externen Eingabe, die einem Programm zugeführt wird.

Jede externe Eingabe muss validiert werden, bevor Sie ihr vertrauen. Diese Validierung muss jedoch nur einmal durchgeführt werden, wenn diese Eingabe empfangen wird.

Es muss nicht die Leistung des Programms beeinflussen, indem es bei jeder Verwendung der Variablen wie Dennis Williamson schlägt vor.


Ich fand die vorhandenen Antworten verwirrend.

Persönlich möchte ich nur etwas haben, das wie C. aussieht und funktioniert.

Dieser Ausschnitt funktioniert mehrmals täglich in der Produktion:

snapshotEvents=true

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

und um alle glücklich zu machen, testete ich:

snapshotEvents=false

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

Was auch gut funktioniert hat.

$snapshotEvents wertet den Inhalt des Wertes der Variable aus. Du brauchst also das $ .

Sie brauchen die Klammern nicht wirklich, ich finde sie nur hilfreich.


Verwenden Sie arithmetische Ausdrücke.

#!/bin/bash

false=0
true=1

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

Ausgabe:

wahr
nicht falsch


Vor langer Zeit, als alles, was wir hatten, war sh , Boolean wurde behandelt, indem man sich auf eine Konvention des test , wo test einen falschen Ausgangsstatus zurückgibt, wenn er ohne Argumente ausgeführt wird. Auf diese Weise kann man sich eine Variable vorstellen, die als falsch definiert ist und auf einen beliebigen Wert als wahr gesetzt ist. Heutzutage ist test in bash und ist allgemein bekannt durch seinen Ein-Zeichen-Alias [ (oder eine ausführbare Datei, die man in Shells ohne Dolmen-Notizen verwenden kann):

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

Wegen Zitationskonventionen bevorzugen Script Writer den Compound-Befehl [[ der test nachahmt test aber eine schönere Syntax hat: Variablen mit Leerzeichen müssen nicht zitiert werden, man kann && und || als logische Operatoren mit seltsamer Priorität, und es gibt keine POSIX-Beschränkungen für die Anzahl der Begriffe.

Um beispielsweise zu bestimmen, ob FLAG gesetzt ist und COUNT eine Zahl größer als 1 ist:

FLAG="u p"
COUNT=3

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

Dieses Material kann verwirrend sein, wenn Leerzeichen, Nulllängen-Strings und Null-Variablen benötigt werden und auch wenn das Skript mit mehreren Shells arbeiten muss.





sh