risposta-alla-domanda-sullo-sviluppo-web-bd.com

Come dichiarare e utilizzare le variabili booleane nello script di Shell?

Ho provato a dichiarare una variabile booleana in uno script Shell usando la seguente sintassi:

variable=$false

variable=$true

È corretto? Inoltre, se volessi aggiornare quella variabile, userei la stessa sintassi? Infine, la seguente sintassi per l'utilizzo di variabili booleane come espressioni corrette:

if [ $variable ]

if [ !$variable ]
728
hassaanm

Risposta rivista (12 febbraio 2014)

the_world_is_flat=true
# ...do something interesting...
if [ "$the_world_is_flat" = true ] ; then
    echo 'Be careful not to fall off!'
fi

Risposta originale

Avvertenze: 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

Da: Utilizzo delle variabili booleane in Bash

Il motivo per cui la risposta originale è inclusa qui è perché i commenti prima della revisione del 12 febbraio 2014 riguardano solo la risposta originale e molti dei commenti sono errati quando associati alla risposta rivista. Ad esempio, il commento di Dennis Williamson su bash built true il 2 giugno 2010 si applica solo alla risposta originale, non alla versione riveduta.

900
miku

TL; DR

bool=true

if [ "$bool" = true ]

Problemi con la risposta di Miku ( originale )

Faccio non raccomandare la risposta accettata1. La sua sintassi è carina ma presenta alcuni difetti.

Diciamo che abbiamo la seguente condizione.

if $var; then
  echo 'Muahahaha!'
fi

Nei seguenti casi2, questa condizione valuterà in true ed eseguirà il comando annidato.

# 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

In genere si desidera che la condizione venga valutata su true quando la variabile "booleana", var in questo esempio, è esplicitamente impostata su true. Tutti gli altri casi sono pericolosamente fuorvianti!

L'ultimo caso (# 5) è particolarmente cattivo perché eseguirà il comando contenuto nella variabile (che è il motivo per cui la condizione restituisce true per i comandi validi3, 4).

Ecco un esempio innocuo:

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!

Citando le tue variabili è più sicuro, ad es. if "$var"; then. Nei casi precedenti, dovresti ricevere un avviso che il comando non è stato trovato. Ma possiamo ancora fare meglio (vedi i miei consigli in fondo).

Vedi anche la spiegazione di Mike Holt sulla risposta originale di Miku.

Problemi con la risposta di Hbar

Questo approccio ha anche un comportamento inaspettato.

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

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

Ci si aspetterebbe che la suddetta condizione venga valutata su false, quindi non si eseguirà mai l'istruzione nidificata. Sorpresa!

Citando il valore ("false"), citando la variabile ("$var"), o usando test o [[ invece di [, non fare la differenza.

Cosa raccomando:

Ecco i modi in cui ti consiglio di controllare i tuoi "booleani". Funzionano come previsto.

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

Sono tutti praticamente equivalenti. Dovrai digitare qualche altra sequenza di tasti rispetto agli approcci nelle altre risposte5 ma il tuo codice sarà più difensivo.


Le note

  1. La risposta di Miku è stata modificata e non contiene più (noti) difetti.
  2. Non una lista esaustiva.
  3. Un comando valido in questo contesto indica un comando che esiste. Non importa se il comando è usato correttamente o in modo errato. Per esempio. man woman sarebbe ancora considerato un comando valido, anche se non esiste una tale pagina man.
  4. Per comandi non validi (inesistenti), Bash si lamenterà semplicemente che il comando non è stato trovato.
  5. Se ti interessa la lunghezza, la prima raccomandazione è la più breve.
657
Dennis

Usa espressioni aritmetiche.

#!/bin/bash

false=0
true=1

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

Produzione:

vero
non falso

42

Per farla breve:

Non ci sono booleani in bash

Quello che bash ha, è espressioni booleane in termini di confronto e condizioni. Detto questo, ciò che puoi dichiarare e confrontare in bash sono stringhe e numeri. Questo è tutto.

Ovunque vedi true o false in bash, è una stringa o un comando/builtin che viene utilizzato solo per il suo codice di uscita.

Questa sintassi ...

if true; then ...

è essenzialmente ...

if COMMAND; then ...

La condizione è vera ogni volta che il comando restituisce il codice di uscita 0. true e false sono compilatori Bash e talvolta anche programmi autonomi che non fanno altro che restituire il codice di uscita corrispondente.

Il condizionale sopra è equivalente a:

COMMAND && ...

Quando si utilizzano le parentesi quadre o il comando test, si fa affidamento sul codice di uscita di tale costrutto. Tieni presente che [ ] e [[ ]] sono anche solo comandi/builtin come qualsiasi altro. Così ...

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

corrisponde a

if COMMAND; then echo yes; fi

e COMMAND qui è [[ 1 == 1 ]]

Il costrutto if..then..fi è solo zucchero sintattico. Puoi sempre eseguire i comandi separati da una doppia e commerciale per lo stesso effetto:

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

Quando si usano true e false in questi costrutti di test, si passa in realtà solo la stringa "true" o "false" al comando di test. Ecco un esempio:

Che ci crediate o no, ma quelle condizioni stanno dando lo stesso stesso risultato :

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

TL; DR; confrontare sempre contro stringhe o numeri

Per renderlo chiaro ai futuri lettori, consiglierei sempre di usare le virgolette su true e false:

FARE

if [[ "${var}" == "true" ]]; then ...
if [[ "${var}" == "false" ]]; then ...
if [[ -n "${var:-}" ]]; then echo "var is not empty" ...

NON

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 `==`

Può essere

if [[ "${var}" != "true" ]]; then ...  # creates impression of booleans. Can be used for strict checking of dangerous operations. This condition is false for anything but the literal string "true". 
20

Molto tempo fa, quando tutto ciò che avevamo era sh, i booleani venivano gestiti facendo affidamento su una convenzione del programma test in cui test restituisce uno stato di uscita falso se eseguito senza argomenti. Ciò consente di pensare a una variabile non impostata come falsa e variabile impostata su qualsiasi valore come vera. Oggi, il test è incorporato in bash ed è comunemente noto con il suo alias di un solo carattere [ (o un eseguibile da usare nelle shell prive di esso, come nota 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

A causa della citazione delle convenzioni, gli scrittori di script preferiscono utilizzare il comando composto [[ che imita test ma ha una sintassi più accurata: le variabili con spazi non devono essere citate, si possono usare && e || come operatori logici con strana precedenza e non ci sono POSIX limitazioni sul numero di termini.

Ad esempio, per determinare se FLAG è impostato e COUNT è un numero maggiore di 1:

FLAG="u p"
COUNT=3

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

Questa roba può creare confusione quando sono necessari tutti gli spazi, le stringhe di lunghezza zero e le variabili nulle e anche quando lo script deve lavorare con diverse shell.

14
Hbar

Come dichiarare e utilizzare le variabili booleane nello script di Shell?

A differenza di molti altri linguaggi di programmazione, Bash non segna le sue variabili per "tipo". [1]

Quindi la risposta è abbastanza chiara. Non esiste boolean variable in bash . Tuttavia:

Utilizzando una dichiarazione dichiarativa, possiamo limitare l'assegnazione del valore alle variabili. [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"

L'opzione r in declare e readonly è usata per dichiarare esplicitamente che le variabili sono readonly. Spero che lo scopo sia chiaro.

11
sjsam

Invece di fingere un booleano e lasciare una trappola per i futuri lettori, perché non usare solo un valore migliore di vero e falso?

Per esempio:

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
6
Pyrolistical

Bill Parker viene votato in ribasso , perché le sue definizioni sono invertite dalla normale convenzione di codice. Normalmente, true è definito come 0 e false è definito come diverso da zero. 1 funzionerà per falso, così come 9999 e -1. Lo stesso con i valori di ritorno della funzione - 0 è successo e qualsiasi valore diverso da zero è negativo. Spiacente, non ho ancora la credibilità della strada per votare o per rispondere direttamente a lui.

Bash raccomanda di utilizzare parentesi doppie ora come un'abitudine invece di parentesi singole, e il link che Mike Holt ha dato spiega le differenze nel modo in cui funzionano. 7.3. Altri operatori di confronto

Per prima cosa, -eq è un operatore numerico, quindi avere il codice

#**** NOTE *** This gives error message *****
The_world_is_flat=0;
if [ "${The_world_is_flat}" -eq true ]; then

emetterà una dichiarazione di errore, in attesa di un'espressione intera. Questo vale per entrambi i parametri, poiché nessuno dei due è un valore intero. Tuttavia, se mettiamo due parentesi quadre, non pubblicherà una dichiarazione di errore, ma produrrà un valore errato (beh, nel 50% delle possibili permutazioni). Valuterà su [[0 -eq true]] = successo, ma anche su [[0 -eq false]] = successo, che è sbagliato (hmmm .... che dire di quel builtin che è un valore numerico?).

#**** NOTE *** This gives wrong output *****
The_world_is_flat=true;
if [[ "${The_world_is_flat}" -eq true ]]; then

Ci sono altre permutazioni del condizionale che forniranno anche output errati. Fondamentalmente, qualsiasi cosa (diversa dalla condizione di errore sopra elencata) imposta una variabile su un valore numerico e la confronta con un builtin vero/falso, o imposta una variabile su un builtin vero/falso e la confronta con un valore numerico. Inoltre, tutto ciò che imposta una variabile su un builtin vero/falso e fa un confronto usando -eq. Quindi evita -eq per i confronti booleani ed evita di usare valori numerici per i confronti booleani. Ecco un riepilogo delle permutazioni che forniranno risultati non validi:

#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

Quindi, ora a cosa funziona. Usa builtin vero/falso sia per il tuo confronto che per le tue valutazioni (come notato da Mike Hunt, non includerle tra virgolette). Quindi utilizzare il segno di uguale o singolo o doppio (= o ==) e le parentesi singola o doppia ([] o [[]]). Personalmente, mi piace il doppio segno di uguale, perché mi ricorda i confronti logici in altri linguaggi di programmazione e le doppie virgolette solo perché mi piace digitare. Quindi questi funzionano:

#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

Ecco qua.

3
Randyman99

In molti linguaggi di programmazione, il tipo booleano è, o è implementato come, un sottotipo di intero, dove true si comporta come 1 e false si comporta come 0:

Matematicamente , l'algebra booleana è simile all'intero aritmetico modulo 2. Pertanto, se un linguaggio non fornisce il tipo booleano nativo, la soluzione più naturale ed efficiente è quella di utilizzare numeri interi. Funziona con quasi tutte le lingue. Ad esempio, in Bash puoi fare:

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

man bash :

((espressione))

L'espressione viene valutata in base alle regole descritte di seguito in VALUTAZIONE ARITMETICA. Se il valore dell'espressione è diverso da zero, lo stato di ritorno è 0; altrimenti lo stato di ritorno è 1. Questo è esattamente equivalente a lasciare "espressione". 

2
Cyker

POSIX (interfaccia del sistema operativo portatile)

Mi manca qui il punto chiave, che è la portabilità. Ecco perché il mio header ha POSIX in se stesso.

In sostanza, tutte le risposte votate sono corrette, con l'eccezione che sono troppo BASH - troppo specifici.

In pratica, desidero solo aggiungere ulteriori informazioni sulla portabilità.


  1. Le parentesi [ e ] come in [ "$var" = true ] non sono necessarie e puoi ometterle e utilizzare direttamente il comando test:

    test "$var" = true && CodeIfTrue || CodeIfFalse
    
  2. Immagina cosa significano le parole true e false per Shell, testalo tu stesso:

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

    Ma usando le virgolette:

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

    Lo stesso vale per:

    echo $(("false"))
    

    Shell non può interpretarlo a parte una stringa. Spero che tu stia prendendo l'idea di quanto sia buono usare le parole chiave appropriate without.

    Ma nessuno ha detto nelle risposte precedenti.

  3. Cosa significa? Bene, molte cose.

    • Dovresti abituarti alle parole chiave booleane che sono effettivamente trattate come numeri, che è true = 0 e false = 1, ricorda che tutti i valori diversi da zero sono trattati come false.

    • Poiché vengono trattati come numeri, dovresti trattarli in questo modo, cioè se definisci una variabile, dì:

      var_a=true
      echo "$var_a"
      
       true
      

      puoi crearne un valore opposto con:

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

      Come puoi vedere tu stesso, Shell stampa true string per la prima volta che lo usi, ma da allora, funziona tutto tramite il numero 0 o 1, rispettivamente.


Infine, cosa dovresti fare con tutte queste informazioni

  • La prima buona abitudine sarebbe assegnare 0 invece di true; 1 anziché false.

  • La seconda buona abitudine sarebbe quella di verificare se la variabile è/non è uguale a zero:

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

Ecco un'implementazione di if true a mano breve.

# 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
}

Esempio 1

my_boolean=true

_if ${my_boolean} && {
    echo "True Is True"
} || {
    echo "False Is False"
}

Esempio 2  

my_boolean=false
! _if ${my_boolean} && echo "Not True is True"
1
llundin

Ecco un semplice esempio che funziona per me:

temp1=true
temp2=false

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

Ecco un miglioramento della risposta originale di miku , che affronta Dennis Williamson le preoccupazioni sul caso, in cui la variabile non è impostata:

the_world_is_flat=true

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

E per verificare se la variabile è false:

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

Su altri casi con un contenuto sgradevole nella variabile, questo è un problema con qualsiasi input esterno alimentato a un programma.

Qualsiasi input esterno deve essere convalidato prima di fidarsi di esso. Ma questa convalida deve essere fatta solo una volta, quando viene ricevuto quell'input.

Non deve influire sulle prestazioni del programma eseguendolo su ogni utilizzo della variabile come Dennis Williamson suggerisce.

0
dolmen

Ho trovato le risposte esistenti confuse.

Personalmente, voglio solo avere qualcosa che sembri e funzioni come C.

Questo frammento funziona più volte al giorno in produzione:

snapshotEvents=true

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

e per far contenti tutti, ho provato:

snapshotEvents=false

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

Che ha funzionato anche bene.

Il $snapshotEvents valuta i contenuti di valore della variabile. Quindi hai bisogno di $.

Non hai davvero bisogno delle parentesi, le trovo solo utili.

0
will

Le mie scoperte e i miei suggerimenti differiscono un po 'dagli altri post. Ho scoperto che potevo usare true/false praticamente come si farebbe in qualsiasi linguaggio "normale", senza il "salto del telaio" suggerito ... Non c'è bisogno di [] o confronti di stringhe esplicite ... Ho provato più distro Linux, ho provato bash, dash e busybox. Ho corso con e senza una botta ... I risultati sono sempre stati gli stessi. Non sono sicuro di cosa stiano parlando i post più votati originali. Forse i tempi sono cambiati e questo è tutto quello che c'è da fare?

Se si imposta una variabile su true, viene valutata su true in un condizionale. Impostalo su false e valuta su false. Molto semplice! L'unica avvertenza, è che una variabile UNDEFINED valuta anche a true! Sarebbe bello se facesse il contrario, ma questo è il trucco - devi solo impostare esplicitamente i tuoi valori booleani su vero o falso.

Vedi l'esempio bash e risultati di seguito. Provalo tu stesso se vuoi confermare ...

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

I rendimenti

when set to 
it evaluates to true
when set to true
it evaluates to true
when set to false
it evaluates to false
0
BuvinJ