Inverti variabile booleana


26

Voglio provare un semplice script

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

Ma quando lo eseguo, se digito true, lo vedrò x="true"e flag="true", ma il ciclo non finisce. Cosa c'è di sbagliato con la sceneggiatura? Come posso invertire correttamente una variabile booleana?



grazie! l'ho preso ora
razza

Risposte:


21

Ci sono due errori nel tuo script. Il primo è che è necessario uno spazio tra !e $flag, altrimenti la shell cerca un comando chiamato !$flag. Il secondo errore è quello -eqper i confronti di numeri interi, ma lo stai usando su una stringa. A seconda della shell, vedrai un messaggio di errore e il ciclo continuerà per sempre perché la condizione [ "$x" -eq "true" ]non può essere vera, oppure ogni valore non intero verrà trattato come 0 e il ciclo uscirà se inserisci una stringa (incluso false) diverso da un numero diverso da 0.

Sebbene ! $flagsia corretto, è una cattiva idea trattare una stringa come un comando. Funzionerebbe, ma sarebbe molto sensibile ai cambiamenti nella tua sceneggiatura, dal momento che dovresti assicurarti che $flagnon possa mai essere altro che trueo false. Sarebbe meglio usare un confronto di stringhe qui, come nel test qui sotto.

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

C'è probabilmente un modo migliore per esprimere la logica che stai cercando. Ad esempio, è possibile creare un ciclo infinito e interromperlo quando si rileva la condizione di terminazione.

while true; do
  read -r x
  if [ "$x" = "true" ]; then break; fi
  echo "$x: false"
done

Con l' [integrato di ksh, [ x -eq y ]restituisce true se l' xespressione aritmetica si risolve nello stesso numero ydell'espressione aritmetica, ad esempio, x=2 true=1+1 ksh -c '[ x -eq true ] && echo yes'se si generasse yes.
Stéphane Chazelas,

10

Sebbene non ci siano variabili booleane in Bash, è molto facile emularle usando la valutazione aritmetica.

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)

Questo funziona anche in ksh. Non sarei sorpreso se funziona in tutte le shell conformi a POSIX, ma non ho verificato lo standard.


1
Funziona ! ! ((val))in Bash, come in C o C ++? Ad esempio, se valè 0, rimane 0 dopo ! !. Se valè 1, rimane 1 dopo ! !. Se valè 8, viene ridotto a 1 dopo ! !. O devo fare un'altra domanda?

1
-1 | In POSIX sh, standalone ((..)) non è definito; rimuovere il riferimento ad esso
LinuxSecurityFreak

Vlastimil la domanda specifica riguarda bash - non posix shell. Perché votare verso il basso quando la domanda non riguarda questo argomento?
Nick Bull,

6

Se si è assolutamente certi che la variabile conterrà 0 o 1, è possibile utilizzare l'operatore bit per bit XOR uguale per alternare tra i due valori:

$ foo=0
$ echo $foo
0
$ ((foo ^= 1))
$ echo $foo
1
$ ((foo ^= 1))
$echo $foo
0

2

Questo funziona per me:

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

Ma, in realtà, ciò che dovresti fare è sostituire il tuo whilecon:

while ! $flag

0

Non esiste un concetto di variabile booleana nella shell.
Le variabili di shell potrebbe essere solo text(una stringa), e, in alcuni casi, che il testo può essere interpretato come un intero ( 1, 0xa, 010, ecc).

Pertanto, a flag=truenon implica affatto veridicità o falsità per il guscio.

Stringa

Quello che si potrebbe fare è un confronto di stringhe [ "$flag" == "true" ]o usare il contenuto variabile in alcuni comandi e verificarne le conseguenze , come eseguire true(perché ci sono sia un eseguibile chiamato trueche uno chiamato false) come comando e verificare se il codice di uscita di quel comando è zero (riuscito).

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

O più breve:

if "$flag"; then ... fi

Se il contenuto di una variabile viene utilizzato come comando, a !potrebbe essere utilizzato per negare lo stato di uscita del comando, se esiste uno spazio tra entrambi ( ! cmd), come in:

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

Lo script dovrebbe cambiare in:

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

Numero intero

Usa valori numerici ed espansioni aritmetiche .

In questo caso, il codice di uscita di $((0))è 1e il codice di uscita di $((1))è 0.
In bash, ksh e zsh l'aritmetica potrebbe essere eseguita all'interno di un ((..))(notare che $manca l'inizio ).

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

Una versione portatile di questo codice è più contorta:

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 potresti fare:

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

In alternativa

Puoi "Invertire una variabile booleana" (a condizione che contenga un valore numerico) come:

((flag=!flag))

Ciò cambierà il valore di flaga 0o 1.


Nota : verificare la presenza di errori in https://www.shellcheck.net/ prima di pubblicare il codice come una domanda, molte volte che è sufficiente per trovare il problema.


0

untilè l'abbreviazione di while !(e until, al contrario di !Bourne), quindi puoi fare:

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

Che dovrebbe essere lo stesso di:

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

Puoi anche scriverlo come:

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

La parte tra until/ whilee donon deve essere un singolo comando.

!è una parola chiave nella sintassi della shell POSIX. Deve essere delimitato, un token separato da quanto segue ed essere il primo token che ha preceduto una pipeline ( ! foo | barannulla la pipeline e foo | ! barnon è valido sebbene alcune shell lo accettino come estensione).

!trueviene interpretato come il !truecomando che è improbabile che esista. ! truedovrebbe funzionare. !(true)dovrebbe funzionare in shell conformi a POSIX poiché !è delimitato in quanto è seguito da un (token, ma in pratica non funziona in ksh/ bash -O extglobdove è in conflitto con l' !(pattern)operatore glob esteso né zsh (tranne che in emulazione) dove è in conflitto glob(qualifiers)con bareglobquale glob(group)senza.


-1
[ ${FOO:-0} == 0 ] && FOO=1 || FOO=0

o anche:

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

dovrebbe fare il lavoro

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.