1291 Stimmen

Wie kann ich boolesche Variablen in einem Shell-Skript deklarieren und verwenden?

Ich habe versucht, eine Boolesche Variable in einem Shellskript unter Verwendung der folgenden Syntax zu deklarieren:

variable=$false

variable=$true

Ist das richtig? Außerdem, wenn ich diese Variable aktualisieren wollte, würde ich die gleiche Syntax verwenden? Ist schließlich die folgende Syntax zur Verwendung von Booleschen Variablen als Ausdrücke korrekt?

if [ $variable ]

if [ !$variable ]

1548voto

miku Punkte 170688

Überarbeitete Antwort (12. Feb 2014)

the_world_is_flat=true
# ...mache etwas Interessantes...
if [ "$the_world_is_flat" = true ] ; then
    echo 'Pass auf, dass du nicht runterfällst!'
fi

Ursprüngliche Antwort

Vorbehalte: https://stackoverflow.com/a/21210966/89391

the_world_is_flat=true
# ...mache etwas Interessantes...
if $the_world_is_flat ; then
    echo 'Pass auf, dass du nicht runterfällst!'
fi

Von: Verwendung von booleschen Variablen in Bash

Der Grund, warum die ursprüngliche Antwort hier aufgeführt ist, liegt darin, dass die Kommentare vor der Überarbeitung am 12. Feb 2014 sich nur auf die ursprüngliche Antwort beziehen, und viele der Kommentare falsch sind, wenn sie mit der überarbeiteten Antwort in Verbindung gebracht werden. Zum Beispiel bezieht sich Dennis Williamsons Kommentar über das bash integrierte true am 2. Jun 2010 nur auf die ursprüngliche Antwort, nicht auf die überarbeitete.

1044voto

Dennis Punkte 51330

Zu lang; nicht gelesen

my_bool=true

if [ "$my_bool" = true ]

Probleme mit Miku's (original) Antwort

Ich empfehle die akzeptierte Antwort1 nicht. Ihre Syntax ist hübsch, aber sie hat einige Fehler.

Angenommen, wir haben die folgende Bedingung.

if $var; then
  echo 'Muahahaha!'
fi

In den folgenden Fällen2 wird diese Bedingung als True ausgewertet und das verschachtelte Kommando ausgeführt.

# Variable var nicht zuvor definiert. Fall 1
var=''  # Äquivalent zu var="".      # Fall 2
var=                                 # Fall 3
unset var                            # Fall 4
var=''           # Fall 5

Typischerweise möchten Sie, dass Ihre Bedingung nur dann als True ausgewertet wird, wenn Ihre "Boolean" Variable, var in diesem Beispiel, explizit auf true gesetzt ist. Alle anderen Fälle sind gefährlich irreführend!

Der letzte Fall (#5) ist besonders böse, weil er den Befehl ausführt, der in der Variable enthalten ist (deshalb wird die Bedingung für gültige Befehle als True ausgewertet3, 4).

Hier ist ein harmloses Beispiel:

var='echo this text will be displayed when the condition is evaluated'
if $var; then
  echo 'Muahahaha!'
fi

# Ergebnisse:
# this text will be displayed when the condition is evaluated
# Muahahaha!

Das Quoten Ihrer Variablen ist sicherer, z.B. if "$var"; then. In den obigen Fällen sollten Sie eine Warnung erhalten, dass der Befehl nicht gefunden wurde. Aber wir können es noch besser machen (siehe meine Empfehlungen unten).

Siehe auch Mike Holts Erklärung von Miku's Originalantwort.

Probleme mit Hbars Antwort

Dieser Ansatz hat auch unerwartetes Verhalten.

var=false
if [ $var ]; then
  echo "This won't print, var is false!"
fi

# Ergebnisse:
# This won't print, var is false!

Man würde erwarten, dass die obige Bedingung als false ausgewertet wird und daher die verschachtelte Anweisung nie ausgeführt wird. Überraschung!

Das Quoting des Wertes ("false"), das Quoting der Variable ("$var") oder die Verwendung von test oder [[ anstelle von [ machen keinen Unterschied.

Was ich empfehle:

Hier sind Wege, wie ich empfehle, Ihre "Booleans" zu überprüfen. Sie funktionieren wie erwartet.

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

Sie sind alle ziemlich gleichwertig. Sie müssen ein paar mehr Tastenanschläge als bei den Ansätzen in den anderen Antworten5 eingeben, aber Ihr Code wird defensiver sein.


Fußnoten

  1. Mikus Antwort wurde seitdem bearbeitet und enthält keine (bekannten) Fehler mehr.
  2. Nicht eine erschöpfende Liste.
  3. Ein gültiger Befehl in diesem Zusammenhang bedeutet ein Befehl, der existiert. Es spielt keine Rolle, ob der Befehl korrekt oder inkorrekt verwendet wird. z.B. man woman würde immer noch als gültiger Befehl gelten, auch wenn keine solche Manpage existiert.
  4. Für ungültige (nicht existierende) Befehle wird Bash einfach bemängeln, dass der Befehl nicht gefunden wurde.
  5. Wenn Ihnen die Länge wichtig ist, ist die erste Empfehlung am kürzesten.

196voto

Mike Holt Punkte 4332

Es scheint hier ein Missverständnis über das integrierte Bash-true zu geben, genauer gesagt darüber, wie Bash Ausdrücke innerhalb von Klammern erweitert und interpretiert.

Der Code in mikus Antwort hat absolut nichts mit dem integrierten Bash-true zu tun, auch nicht mit /bin/true oder einer anderen Variante des true-Befehls. In diesem Fall ist true nichts weiter als eine einfache Zeichenkette, und es wird weder durch die Variablenzuweisung noch durch die Auswertung des bedingten Ausdrucks jemals ein Aufruf des true-Befehls/integriertes gemacht.

Der folgende Code ist funktional identisch mit dem Code in mikus Antwort:

the_world_is_flat=ja
if [ "$the_world_is_flat" = ja ]; then
    echo 'Pass auf, dass du nicht runterfällst!'
fi

Der einzige Unterschied hier besteht darin, dass die vier verglichenen Zeichen 'j', 'a', 'e' und 'h' anstelle von 't', 'r', 'u' und 'e' sind. Das war's. Es wird kein Versuch unternommen, einen Befehl oder ein eingebautes Programm namens ja aufzurufen, und es findet (im Beispiel von miku) keine spezielle Behandlung statt, wenn Bash das Token true analysiert. Es ist einfach eine Zeichenkette und eine völlig beliebige noch dazu.

Update (2014-02-19): Nachdem ich dem Link in mikus Antwort gefolgt bin, verstehe ich jetzt, woher etwas von der Verwirrung kommt. Mikus Antwort verwendet eckige Klammern, aber das von ihm verlinkte Code-Snippet verwendet keine Klammern. Es lautet einfach:

the_world_is_flat=true
if $the_world_is_flat; then
  echo 'Pass auf, dass du nicht runterfällst!'
fi

Beide Code-Snippets verhalten sich genauso, aber die Klammern ändern vollständig, was im Hintergrund passiert.

Das macht Bash in jedem Fall:

Keine Klammern:

  1. Erweitere die Variable $the_world_is_flat zur Zeichenkette "true".
  2. Versuche, die Zeichenkette "true" als Befehl zu analysieren.
  3. Den true-Befehl finden und ausführen (entweder ein eingebauter oder /bin/true, je nach Bash-Version).
  4. Vergleiche den Exit-Code des true-Befehls (der immer 0 ist) mit 0. Beachte, dass in den meisten Shells ein Exit-Code von 0 Erfolg und alles andere Misserfolg bedeutet.
  5. Da der Exit-Code 0 (Erfolg) war, führe die then-Klausel des if-Statements aus.

Klammern:

  1. Erweitere die Variable $the_world_is_flat zur Zeichenkette "true".
  2. Analysiere den nun vollständig erweiterten bedingten Ausdruck, der die Form string1 = string2 hat. Der =-Operator ist Bashes Zeichenkettenvergleichs-Operator. Also...
  3. Führe einen Zeichenkettenvergleich zwischen "true" und "true" durch.
  4. Ja, die beiden Zeichenketten waren identisch, also ist der Wert der Bedingung wahr.
  5. Führe die then-Klausel des if-Statements aus.

Der Code ohne Klammern funktioniert, weil der true-Befehl einen Exit-Code von 0 zurückgibt, was Erfolg bedeutet. Der in Klammern stehende Code funktioniert, weil der Wert von $the_world_is_flat mit der Zeichenkettenliteralen true auf der rechten Seite des = identisch ist.

Um das zu verdeutlichen, betrachte die folgenden beiden Code-Snippets:

Dieser Code (wenn mit Root-Berechtigungen ausgeführt) wird deinen Computer neu starten:

var=neustarten
if $var; then
  echo 'Muahahaha! Du gehst unter!'
fi

Dieser Code druckt nur "Schöner Versuch." Der Neustart-Befehl wird nicht aufgerufen.

var=neustarten
if [ $var ]; then
  echo 'Schöner Versuch.'
fi

Update (2014-04-14) Um die Frage in den Kommentaren zur Differenz zwischen = und == zu beantworten: Nach meinem Kenntnisstand 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.

Es sei jedoch darauf hingewiesen, dass ich speziell über die = und == Zeichenkettenvergleichsoperatoren in [ ] oder [[ ]]-Tests spreche. Ich möchte nicht behaupten, dass = und == überall in Bash austauschbar sind.

Zum Beispiel kann man offensichtlich keine Variablenzuweisung mit == machen, wie z.B. var=="foo" (technisch gesehen kannst du das tun, aber der Wert von var wird "=foo" sein, weil Bash hier keinen ==-Operator sieht, sondern einen = (Zuweisung) -Operator, gefolgt von dem Literalwert ="foo", was einfach zu "=foo" wird).

Außerdem, obwohl = und == austauschbar sind, sollte man beachten, dass die Funktionsweise dieser Tests davon abhängt, ob sie innerhalb von [ ] oder [[ ]] verwendet werden und ob die Operanden in Anführungszeichen stehen. Mehr dazu findest du im Advanced Bash Scripting Guide: 7.3 Other Comparison Operators (scrolle zur Diskussion über = und ==).

82voto

Quolonel Questions Punkte 6006

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:

true
not false

71voto

Hubert Grzeskowiak Punkte 12801

Langer Rede kurzer Sinn:

Es gibt keine Booleans in Bash

Die Befehle true und false

In Bash gibt es Boolesche Ausdrücke in Form von Vergleichen und Bedingungen. Was du jedoch in Bash deklarieren und vergleichen kannst, sind nur Strings und Zahlen. Das war's.

Überall wo du in Bash true oder false siehst, handelt es sich entweder um einen String oder einen Befehl/ein integriertes Programm, das nur für seinen Exit-Code verwendet wird.

Diese Syntax...

if true; then ...

ist im Grunde genommen...

if BEFEHL; then ...

wobei der Befehl true ist. Die Bedingung ist wahr, wenn der Befehl den Exit-Code 0 zurückgibt. true und false sind Bash-Builtin-Befehle und manchmal auch eigenständige Programme, die nichts weiter tun als den entsprechenden Exit-Code zurückzugeben.


Bedingungen in if..then..fi

Wenn du eckige Klammern oder den test-Befehl verwendest, verlässt du dich auf den Exit-Code dieses Konstrukts. Bedenke, dass [ ] und [[ ]] genauso Befehle/Integrierte wie alle anderen sind. Also ...

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

entspricht

if BEFEHL; then echo ja; fi

und der BEFEHL hier ist [[ mit den Parametern 1 == 1 ]]

Das Konstrukt if..then..fi ist nur syntaktischer Zucker. Du kannst die Befehle immer einfach durch ein doppeltes Kaufmanns-Und für den gleichen Effekt ausführen:

[[ 1 == 1 ]] && echo ja

Wenn du true und false in diesen Testkonstrukten verwendest, gibst du tatsächlich nur den String "true" oder "false" an den Test-Befehl weiter. Hier ist ein Beispiel:

Glaub es oder nicht, aber diese Bedingungen ergeben alle das selbe Ergebnis:

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

Zusammenfassung; vergleiche immer mit Strings oder Zahlen

Um dies für zukünftige Leser klarer zu machen, würde ich empfehlen, immer Anführungszeichen um true und false zu verwenden:

MACH

if [[ "${var}" == "true" ]]; then ...
if [[ "${var}" == "false" ]]; then ...
if [[ "${var}" == "ja" ]]; then ...
if [[ "${var}" == "USE_FEATURE_X" ]]; then ...
if [[ -n "${var:-}" ]]; then echo "var ist nicht leer" ...

MACH NICHT

# Verwende in Bash immer doppelte eckige Klammern!
if [ ... ]; then ...
# Dies ist nicht so klar oder durchsuchbar wie -n
if [[ "${var}" ]]; then ...
# Erzeugt den Eindruck von Booleans
if [[ "${var}" != true ]]; then ...
# `-eq` ist für Zahlen und liest sich nicht so einfach wie `==`
if [[ "${var}" -eq "true" ]]; then ...

Vielleicht

# Erzeugt den Eindruck von Booleans.
# Es kann für das strikte Überprüfen gefährlicher Operationen verwendet werden.
# Diese Bedingung ist nur für den wörtlichen String "true" falsch.
if [[ "${var}" != "true" ]]; then ...

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X