Boolesche Variable invertieren

26

Ich möchte ein einfaches Skript ausprobieren

flag=false
while !$flag
do
   read x
   if [ "$x" -eq "true" ]
   then
     flag=true
   fi
   echo "${x} : ${flag}"
done

Aber wenn ich es laufen lasse, wenn ich tippe true, sehe ich das x="true"und flag="true", aber der Zyklus endet nicht. Was ist los mit dem Skript? Wie kann ich eine boolesche Variable richtig invertieren?

schwedisch
quelle
Danke! Ich habe es jetzt
Schwedisch

Antworten:

21

Es gibt zwei Fehler in Ihrem Skript. Das erste ist, dass Sie ein Leerzeichen zwischen !und benötigen $flag, andernfalls sucht die Shell nach einem aufgerufenen Befehl !$flag. Der zweite Fehler -eqbetrifft Ganzzahlvergleiche, aber Sie verwenden ihn für eine Zeichenfolge. Abhängig von Ihrer Shell wird entweder eine Fehlermeldung angezeigt und die Schleife für immer fortgesetzt, da die Bedingung [ "$x" -eq "true" ]nicht wahr sein kann, oder jeder nicht ganzzahlige Wert wird als 0 behandelt und die Schleife wird beendet, wenn Sie eine Zeichenfolge (einschließlich false) eingeben. anders als eine Zahl von 0.

Solange ! $flages richtig ist, ist es eine schlechte Idee, eine Zeichenfolge als Befehl zu behandeln. Es würde funktionieren, wäre aber sehr empfindlich gegenüber Änderungen in Ihrem Skript, da Sie sicherstellen müssten, dass $flagdies niemals etwas anderes als trueoder sein kann false. Es wäre besser, hier einen Zeichenfolgenvergleich zu verwenden, wie im folgenden Test.

flag=false
while [ "$flag" != "true" ]
do
   read x
   if [ "$x" = "true" ]
   then
     flag=true
   fi
   echo "${x} : ${flag}"
done

Es gibt wahrscheinlich eine bessere Möglichkeit, die Logik auszudrücken, nach der Sie suchen. Beispielsweise können Sie eine Endlosschleife erstellen und diese unterbrechen, wenn Sie die Beendigungsbedingung erkennen.

while true; do
  read -r x
  if [ "$x" = "true" ]; then break; fi
  echo "$x: false"
done
Gilles 'SO - hör auf böse zu sein'
quelle
Mit dem [eingebauten Wert von kshwird [ x -eq y ]true zurückgegeben, wenn der xarithmetische Ausdruck in dieselbe Zahl aufgelöst wird wie der yarithmetische Ausdruck, sodass beispielsweise x=2 true=1+1 ksh -c '[ x -eq true ] && echo yes'yes ausgegeben wird.
Stéphane Chazelas
10

Während es in Bash keine Booleschen Variablen gibt, ist es sehr einfach, sie mithilfe der arithmetischen Auswertung zu emulieren.

flag= # False
flag=0 # False
flag=1 # True (actually any integer number != 0 will do, but see remark below about toggling)
flag="some string" # Maybe False (make sure that the string isn't interpreted as a number)

if ((flag)) # Test for True
then
  : # do something
fi

if ! ((flag)) # Test for False
then
  : # do something
fi

flag=$((1-flag)) # Toggle flag (only works when flag is either empty or unset, 0, 1, or a string which doesn't represent a number)

Dies funktioniert auch in ksh. Es würde mich nicht wundern, wenn es in allen POSIX-kompatiblen Shells funktioniert, aber den Standard nicht überprüft hat.

ack
quelle
1
Funktioniert es ! ! ((val))in Bash wie in C oder C ++? Wenn zum Beispiel val0 ist, bleibt es danach 0 ! !. Wenn val1 ist, bleibt es 1 danach ! !. Wenn val8 ist, wird es danach auf 1 heruntergezogen ! !. Oder muss ich eine andere Frage stellen?
1
-1 | In POSIX sh ist Standalone ((..)) undefiniert. Bitte entfernen Sie den Verweis darauf
LinuxSecurityFreak
Vlastimil die Frage ist speziell über Bash - nicht Posix Shell. Warum abstimmen, wenn die Frage nicht zu diesem Thema gehört?
Nick Bull
6

Wenn Sie absolut sicher sind, dass die Variable entweder 0 oder 1 enthält, können Sie mit dem bitweisen XOR-Gleichheitsoperator zwischen den beiden Werten wechseln:

$ foo=0
$ echo $foo
0
$ ((foo ^= 1))
$ echo $foo
1
$ ((foo ^= 1))
$echo $foo
0
user971217
quelle
2

Das funktioniert bei mir:

flag=false
while [[ "$flag" == "false" ]]
do
   read x
   if [[ "$x" == "true" ]]
   then
     flag=true
   fi
   echo "${x} : ${flag}"
done

Aber eigentlich sollten Sie Ihre ersetzen whiledurch:

while ! $flag
ДМИТРИЙ МАЛИКОВ
quelle
0

Es gibt kein Konzept für eine Boolesche Variable in der Shell.
Shell - Variablen könnten nur sein text(eine Zeichenfolge), und in einigen Fällen, dass Text als Integer interpretiert werden ( 1, 0xa, 010, etc.).

Daher flag=trueimpliziert a überhaupt keine Wahrhaftigkeit oder Falschheit der Hülle.

String

Sie können entweder einen Zeichenfolgenvergleich durchführen [ "$flag" == "true" ]oder den Variableninhalt in einem Befehl verwenden und dessen Konsequenzen überprüfen , z. B. "execute" true(da sowohl eine ausführbare als auch trueeine aufgerufene Datei vorhanden ist false) als Befehl und prüfen, ob der Beendigungscode dieses Befehls Null ist (erfolgreich).

$flag; if [ "$?" -eq 0 ]; then ... fi

Oder kürzer:

if "$flag"; then ... fi

Wenn der Inhalt einer Variablen als Befehl verwendet wird, kann a !verwendet werden, um den Beendigungsstatus des Befehls zu negieren, wenn zwischen both ( ! cmd) ein Leerzeichen vorhanden ist , wie in:

if ! "$flag"; then ... fi

Das Skript sollte folgendermaßen geändert werden:

flag=false
while ! "$flag" 
do
   read x
   if [ "$x" == "true" ]
   then
     flag=true
   fi
   echo "${x} : ${flag}"
done

Ganze Zahl

Verwenden Sie numerische Werte und arithmetische Erweiterungen .

In diesem Fall sind der Exit-Code von $((0))is 1und der Exit-Code von $((1))is 0.
In bash, ksh und zsh könnte die Arithmetik innerhalb von a ausgeführt werden ((..))(beachten Sie, dass der Anfang $fehlt).

flag=0; if ((flag)); then ... fi

Eine portable Version dieses Codes ist komplizierter:

flag=0; if [ "$((flag))" -eq 0 ]; then ... fi      # test for a number
flag=0; if [ "$((flag))"  == 0 ]; then ... fi      # test for the string "0"

In bash / ksh / zsh können Sie Folgendes tun:

flag=0    
while ((!flag)) 
do
   read x
   [ "$x" == "true" ] && flag=1
   echo "${x} : ${flag}"
done

Alternative

Sie können "eine boolesche Variable invertieren" (vorausgesetzt, sie enthält einen numerischen Wert) als:

((flag=!flag))

Das ändert den Wert von flagentweder 0oder 1.


Hinweis : Überprüfen Sie https://www.shellcheck.net/ auf Fehler, bevor Sie Ihren Code als Frage veröffentlichen. Dies ist oft genug, um das Problem zu finden.

Isaac
quelle
0

untilist die Abkürzung für while !(und untilim Gegensatz zu !Bourne), so dass Sie Folgendes tun können:

flag=false
until "$flag"
do
   IFS= read -r x
   if [ "$x" = true ]
   then
     flag=true
   fi
   printf '%s\n' "$x : $flag"
done

Welches sollte das gleiche sein wie:

flag=false
while ! "$flag"
do
   IFS= read -r x
   if [ "$x" = true ]
   then
     flag=true
   fi
   printf '%s\n' "$x : $flag"
done

Sie könnten es auch schreiben als:

until
   IFS= read -r x
   printf '%s\n' "$x"
   [ "$x" = true ]
do
   continue
done

Der Teil zwischen until/ whileund domuss kein einzelner Befehl sein.

!ist ein Schlüsselwort in der POSIX-Shell-Syntax. Es muss abgegrenzt sein, ein von dem Folgenden getrenntes Token und das erste Token, das einer Pipeline vorausgeht ( ! foo | barnegiert die Pipeline und foo | ! barist ungültig, obwohl einige Shells sie als Erweiterung akzeptieren).

!truewird als der !trueBefehl interpretiert , dessen Existenz unwahrscheinlich ist. ! truesollte arbeiten. !(true)sollte in POSIX-kompatiblen Shells funktionieren !, da dies durch ein (Token abgegrenzt ist. In der Praxis funktioniert es jedoch nicht in ksh/ bash -O extglobwo es mit dem !(pattern)erweiterten Glob-Operator oder zsh (außer in der Sh-Emulation) in Konflikt steht, wo es glob(qualifiers)mit bareglobqualund in Konflikt steht glob(group)ohne.

Stéphane Chazelas
quelle
-1
[ ${FOO:-0} == 0 ] && FOO=1 || FOO=0

oder auch:

[ ${FOO:-false} == false ] && FOO=true || FOO=false

sollte den Job machen

Slaxor
quelle