Come posso dichiarare e usare variabili booleane in uno script di shell?


981

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

variable=$false

variable=$true

È corretto? Inoltre, se volessi aggiornare quella variabile, utilizzerei la stessa sintassi? Infine, la seguente sintassi per l'uso delle variabili booleane come espressioni è corretta?

if [ $variable ]

if [ !$variable ]

73
ATTENZIONE! truee falsenel contesto della maggior parte dei frammenti di seguito sono solo stringhe semplici, non il bash built-ins!!! Si prega di leggere la risposta di Mike Holt di seguito. (Questo è un esempio in cui una risposta altamente votata e accettata è fonte di confusione per l'IMHO e mette in ombra i contenuti perspicaci nelle risposte meno votate)
mjv

7
@mjv La maggior parte della confusione su questa domanda (e la risposta di Miku) è dovuta al fatto che Miku ha rivisto la sua risposta ad un certo punto dopo che sono stati pubblicati diversi commenti che descrivono come la risposta di Miku ha comportato la chiamata al bash incorporato true. Si scopre che la risposta originale di Miku in realtà ha chiamato il truebuilt-in, ma la risposta rivista non l'ha fatto. Ciò ha comportato che i commenti sembravano sbagliati sul funzionamento del codice di Miku. Da allora la risposta di Miku è stata modificata per mostrare esplicitamente sia il codice originale che quello rivisto. Spero che questo metta fine alla confusione una volta per tutte.
Mike Holt,

2
[ true ] && echo yes, true is truee (upppsss) [ false ] && echo yes, false is also true. / bin / true e / bin / false fornisce un codice di ritorno $? per funzioni non a confronto.
fcm

Se var è impostato variable=somethingsu quanto è vero, se non impostato variable=sarebbe falso [[ $variable ]] && echo true || echo falsee contrario[[ ! $variable ]] && echo false || echo true
Ivan

Risposte:


1203

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 di 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 sul bash incorporato trueil 2 giugno 2010 si applica solo alla risposta originale, non alla revisione.


37
Per spiegare cosa sta succedendo: l' ifistruzione sta eseguendo il contenuto della variabile che è incorporata in Bash true. Qualsiasi comando potrebbe essere impostato come valore della variabile e il suo valore di uscita verrebbe valutato.
In pausa fino a nuovo avviso.

7
@pms Gli operatori "-o" e "-a" sono solo per il comando "test" (aka "[]"). Invece, questo è "if + command", senza il "test". (Come "se grep foo file; then ...".) Quindi, usa il normale &&e gli ||operatori: # t1=true; t2=true; f1=false;# if $t1 || $f1; then echo is_true ; else echo is_false; fi; (restituisce "true", poiché t1 = true) # if $t1 && $f1 || $t2; then echo is_true ; else echo is_false; fi (restituisce "true", poiché t2 = true) . Ancora una volta, funziona SOLO perché "true" / "false" sono built-in bash (che restituiscono true / false). Non puoi usare "if $ var ..." a meno che var sia un cmd (vale a dire vero o falso)
michael

14
-1, vedi la mia risposta per una spiegazione.
Dennis,

3
Molte informazioni errate, qui. / bin / true non viene utilizzato in modo efficace. Vedi la risposta di Dennis.
aprile

1
Questo codice non è lo stesso e non funziona allo stesso modo dell'articolo collegato. Il codice collegato chiama un programma con il nome memorizzato in una variabile ma il codice in questa risposta è solo un confronto di stringhe.
Quolonel Domande

795

TL; DR

bool=true

if [ "$bool" = true ]

Problemi con la risposta ( originale ) di Miku

Io non consiglio la risposta accettata 1 . La sua sintassi è carina, ma presenta alcuni difetti.

Supponiamo che abbiamo le seguenti condizioni.

if $var; then
  echo 'Muahahaha!'
fi

Nei seguenti casi 2 , questa condizione verrà valutata come vera ed eseguirà il comando nidificato.

# 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 varvenga valutata come vera quando la variabile "Booleana", 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 (motivo per cui la condizione viene valutata vera per i comandi validi 3, 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 di meglio (vedi i miei consigli in fondo).

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

Problemi con la risposta di Hbar

Questo approccio ha anche comportamenti imprevisti.

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

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

Ti aspetteresti che la condizione di cui sopra valuti false, quindi non esegui mai l'istruzione nidificata. Sorpresa!

Citando il valore ( "false"), quotando la variabile ( "$var") o usando testo [[invece di [, non fa differenza.

Quello che mi 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 praticamente tutti equivalenti. Dovrai digitare un numero maggiore di tasti rispetto agli approcci delle altre risposte 5 , ma il tuo codice sarà più difensivo.


Le note

  1. Da allora la risposta di Miku è stata modificata e non contiene più difetti (noti).
  2. Non un elenco esaustivo.
  3. Un comando valido in questo contesto indica un comando esistente. Non importa se il comando viene utilizzato correttamente o in modo errato. Ad esempio, man womansarebbe comunque 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.

8
L'uso ==con [o testnon è portatile. Considerare la portabilità è l'unico vantaggio [/ testrispetto a cui [[attenersi =.
Chepner,

2
@Scott Uso il pesce come shell principale, che secondo me ha un linguaggio di scripting sano rispetto a bash.
Dennis,

1
Sì, non sono riuscito a trovare nei commenti alcun apprezzamento per questa battuta nascosta, quindi ho dovuto sottolineare =)
Kranach,

5
Per me, concettualmente è più facile capire se uso bool = "true". Quindi è chiaro che è solo una stringa e non un valore speciale o incorporato.
Wisbucky,

1
@dolmen assolutamente, valutare l'input non è così rischioso quando si controlla l'input, ma lo considero ancora una cattiva pratica che dovrebbe essere evitata se può essere facilmente evitata. Qualcuno che ha sempre visto e usato il vecchio stile potrebbe non conoscere i suoi difetti che possono causare comportamenti inaspettati.
Dennis

175

Sembra che qui ci siano dei malintesi sull'integrato di Bash truee, più specificamente, su come Bash espande e interpreta le espressioni tra parentesi.

Il codice in risposta di Miku non ha assolutamente nulla a che fare con il builtin Bash true, né /bin/true, né alcun altro sapore del truecomando. In questo caso, truenon è altro che una semplice stringa di caratteri e non trueviene mai effettuata alcuna chiamata al comando / builtin, né dall'assegnazione delle variabili, né dalla valutazione dell'espressione condizionale.

Il seguente codice è funzionalmente identico al codice nella risposta del miku:

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

L' unica differenza qui è che i quattro caratteri che vengono confrontati sono 'y', 'e', ​​'a' e 'h' invece di 't', 'r', 'u' ed 'e'. Questo è tutto. Non è stato fatto alcun tentativo di chiamare un comando o un builtin chiamato yeah, né (nell'esempio di miku) non c'è alcun tipo di gestione speciale in corso quando Bash analizza il token true. È solo una stringa, e completamente arbitraria.

Aggiornamento (19-02-2014): dopo aver seguito il link nella risposta di Miku , ora vedo da dove proviene parte della confusione. La risposta di Miku usa parentesi singole, ma lo snippet di codice a cui si collega non usa parentesi. È appena:

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

Entrambi i frammenti di codice si comporteranno allo stesso modo, ma le parentesi cambiano completamente ciò che accade sotto il cofano.

Ecco cosa sta facendo Bash in ogni caso:

Nessuna parentesi:

  1. Espandi la variabile $the_world_is_flatalla stringa "true".
  2. Tentativo di analizzare la stringa "true"come comando.
  3. Trova ed esegui il truecomando (incorporato o /bin/true, a seconda della versione di Bash).
  4. Confronta il codice di uscita del truecomando (che è sempre 0) con 0. Ricorda che nella maggior parte delle shell, un codice di uscita 0 indica il successo e qualsiasi altra cosa indica il fallimento.
  5. Dal momento che il codice di uscita è 0 (successo), eseguire l' ifdella dichiarazione thenclausola

Parentesi:

  1. Espandi la variabile $the_world_is_flatalla stringa "true".
  2. Analizza l'espressione condizionale ora completamente espansa, che è della forma string1 = string2. L' =operatore è l' operatore di confronto delle stringhe di bash . Così...
  3. Fai un confronto tra stringhe su "true"e "true".
  4. Sì, le due stringhe erano uguali, quindi il valore del condizionale è vero.
  5. Eseguire l' ifdella dichiarazione thenclausola.

Il codice senza parentesi funziona, poiché il truecomando restituisce un codice di uscita pari a 0, che indica l'esito positivo. Il codice tra parentesi funziona perché il valore di $the_world_is_flatè identico alla stringa letterale truesul lato destro di =.

Solo per riportare il punto a casa, considera i seguenti due frammenti di codice:

Questo codice (se eseguito con i privilegi di root) riavvierà il tuo computer:

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

Questo codice stampa semplicemente "Bel tentativo". Il comando reboot non viene chiamato.

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

Aggiornamento (14-04-2014) Per rispondere alla domanda nei commenti sulla differenza tra =e ==: AFAIK, non c'è differenza. L' ==operatore è un sinonimo specifico di Bash =e, per quanto ho visto, funzionano esattamente allo stesso modo in tutti i contesti.

Si noti, tuttavia, che sto parlando in particolare degli operatori di confronto di stringhe =e ==utilizzati in uno dei test [ ]o [[ ]]. Non lo sto suggerendo =e ==sono intercambiabili ovunque in bash.

Ad esempio, ovviamente non è possibile eseguire un'assegnazione variabile con ==, come var=="foo"(bene tecnicamente puoi farlo, ma il valore di varsarà "=foo", perché Bash non sta vedendo un ==operatore qui, sta vedendo un =operatore (assegnazione), seguito da il valore letterale ="foo", che diventa "=foo").

Inoltre, anche se =e ==sono intercambiabili, si dovrebbe tenere a mente che come tali test lavoro non dipende dal fatto che lo si usa all'interno [ ]o [[ ]], e anche sulla necessità o meno gli operandi sono quotati. Puoi leggere ulteriori informazioni al riguardo nella Guida agli script avanzati di Bash: 7.3 Altri operatori di confronto (scorri verso il basso fino alla discussione di =e ==).


L'approccio senza parentesi ha anche il vantaggio di permetterti di scrivere una $the_world_is_flat && echo "you are in flatland!"
riga

9
Vero. Sebbene, non sto sostenendo (o contro) nessuno dei due approcci. Volevo solo chiarire alcune delle informazioni sbagliate che vengono votate qui, in modo che le persone che inciampano su questo argomento in seguito non se ne vadano con un sacco di idee sbagliate su come tutto questo funziona.
Mike Holt,

1
Il motivo della confusione è che la risposta originale di Miku è rimasta per 4 anni. Tutti i riferimenti al builtin truesono stati fatti riguardo alla risposta originale. (La risposta rivista del 12 febbraio 2014 non è stata inviata da miku.) Ho modificato la risposta per includere sia l'originale che la rivista. Quindi i commenti delle persone hanno un senso.
wisbucky,

1
Leggendo le risposte offerte qui, ho l'impressione che non ci sia nulla di simile al reale true. C'è un modo? Ho il sospetto che molti programmatori che sono abituati a linguaggi più rigorosi vedendo questa risposta per aiutarli a mescolare un po 'di bashcolla per rendere un po' più semplice la loro vita vorrebbero un ===operatore in modo che stringhe e "booleani" non siano effettivamente intercambiabili. Dovrebbero solo bastone a 0 e 1 e l'uso (( $maybeIAmTrue ))come suggerito in Quolonel domanda 's risposta ?
SeldomNeedy,

2
Per rispondere al commento di SeldomNeedy, sì, puoi usare il reale true, ma generalmente non come qualcosa per confrontare una variabile, poiché il reale truenon ha alcun valore in sé. Tutto ciò che fa è impostare lo stato di uscita su 0, indicando il successo. Vale la pena notare che è essenzialmente equivalente al cosiddetto "comando null", o :. Per quanto riguarda l'utilizzo di 0e 1, questo è quello che faccio in tutti i miei script in questi giorni in cui ho bisogno di booleani. E io uso l' (( ))operatore invece di [[ ]]valutare. Quindi, per esempio, se ho flag=0, posso farloif (( flag )); then ...
Mike Holt,

57

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


3
pro: (1.) il comportamento è simile al modo in cui C gestisce i bool, (2.) la sintassi è molto concisa / minima (non richiede una variabile della mano destra e operatori come '=' o '=='), (3 .) <soggettivo> per me capisco cosa succede senza una spiegazione a lungo termine ... contrasto con le risposte di Miku e Dennis che sembrano richiedere spiegazioni a lungo termine </subject>>
Trevor Boyd Smith

3
@TrevorBoydSmith Perché non hai semplicemente detto "pro: tutto, contro: niente". Consentirebbe di risparmiare costi di ammortamento sulla tastiera e monitorare a lungo termine.
Quolonel Questions,

4
Per un uso interattivo, come una riga, assicurati di lasciare uno spazio dopo !, o farà l'espansione della cronologia. ((! foo))funziona, così fa ! ((foo)). Adoro questa soluzione, BTW. Finalmente un modo conciso per fare variabili booleane. ((foo || bar))funziona come previsto.
Peter Cordes,

5
(())espande le variabili in modo ricorsivo, cosa che non mi aspettavo. foo=bar; bar=baz; ((foo)) && echo echonon stampa nulla, ma è vero con baz=1. Quindi puoi supportare foo=truee foo=falseanche 0 o 1 facendo true=1.
Peter Cordes,

2
@quolonel grazie per la risorsa molto utile. Ovviamente la mia comprensione è limitata: la natura umana è limitata in ogni comprensione, non importa il dominio. Tuttavia, ti dispiacerebbe dirmi quale delle mie affermazioni ti porta a supporre che la mia comprensione di questa particolare questione sia incompleta?
Hubert Grzeskowiak,

35

Per farla breve:

Non ci sono booleani in Bash

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 tu veda trueo falsein 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. truee falsesono built-in di 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 parentesi quadre o il testcomando, si fa affidamento sul codice di uscita di quel costrutto. Tienilo a mente [ ]e [[ ]]sono anche solo comandi / comandi come tutti gli altri. Così ...

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

corrisponde a

if COMMAND; then echo yes; fi

e il COMMANDqui è[[ 1 == 1 ]]

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

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

Quando si utilizza truee falsein questi costrutti di test si passa effettivamente solo alla stringa "true"o "false"al comando testing. Ecco un esempio:

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

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

TL; DR; confronta sempre con stringhe o numeri

Per chiarire questo aspetto ai futuri lettori, consiglierei sempre di usare le virgolette truee false:

FARE

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

DO NOT

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. It can be used for strict checking of dangerous operations. This condition is false for anything but the literal string "true".

Preferisco usare Te Fchiarire che quelli non sono veri valori booleani.
phk,

1
Non posso essere d'accordo con "usa sempre doppie parentesi in bash". In effetti in quasi tutti gli script che ho scritto sto usando parentesi singole, tranne quando ho bisogno di fare corrispondenze di pattern. Penso che si dovrebbe capire la differenza tra [(cioè test) [[e usare quello adatto alle sue necessità.
Weijun Zhou,

@WeijunZhou elaborando la mente in quali casi le parentesi singole sono migliori?
Hubert Grzeskowiak

È più di un gusto personale, trovo solo che sia troppo audace dire "Usa sempre parentesi quadre doppie in bash". Ma ci sono alcuni casi limite che ho usato. Le parentesi singole consentono di specificare il test stesso in una var. Come esempio semplificato, consideraif ....; then mytest='-gt'; else mytest='-eq'; fi; #several lines of code; if [ "$var1" "$mytest" "$var2" ]; then ...; fi
Weijun Zhou

1
@WeijunZhou Il tuo esempio è un argomento forte contro parentesi quadre singole. Rende il codice molto più difficile da comprendere e apre la finestra aperta agli errori. Le parentesi doppie sono più rigide e incoraggiano un codice più pulito.
Hubert Grzeskowiak

18

Molto tempo fa, quando tutto ciò che avevamo era sh, i booleani venivano gestiti facendo affidamento su una convenzione del testprogramma in cui testrestituiva uno stato di uscita falso se eseguito senza alcun argomento.

Ciò consente di pensare a una variabile non impostata come falsa e la variabile impostata su qualsiasi valore come vera. Oggi testè incorporato in Bash ed è comunemente noto con il suo alias a un carattere [(o un eseguibile da usare nelle shell che lo mancano, come osserva 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 delle convenzioni di citazione, gli autori di script preferiscono utilizzare il comando composto [[che imita test, ma ha una sintassi migliore: non è necessario che le variabili con spazi siano citate; si possono usare &&e ||come operatori logici con strana precedenza e non ci sono limiti POSIX 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

Queste cose possono creare confusione quando sono necessari spazi, stringhe di lunghezza zero e variabili null e anche quando lo script deve funzionare con diverse shell.


3
[non è solo un alias dentro bash. Questo alias esiste anche come file binario (o come collegamento che punta a) e può essere utilizzato con il nudo sh. Controllare ls -l /usr/bin/\[. Con bash/ zshdovresti invece usare [[quello che è un vero interno puro ed è molto più potente.
dolmen,

1
@dolmen [ed testè anche un COMANDO INCORPORATO DI Bash SHELL secondo la pagina del manuale di Bash, quindi non dovrebbero esserci problemi nelle prestazioni. Stessa cosa con ad esempio Dash. (/ bin / sh può essere solo un collegamento simbolico a / bin / dash). Per usare l'eseguibile devi usare il percorso completo, ad es /usr/bin/\[.
jarno

12

Come posso dichiarare e usare variabili booleane in uno script di shell?

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

Quindi la risposta è abbastanza chiara. Non esiste alcuna variabile booleana in Bash.

Però:

Usando un'istruzione declare, 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 terminates
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' ropzione in declaree readonlyviene utilizzata per indicare esplicitamente che le variabili sono di sola lettura . Spero che lo scopo sia chiaro.


1
Perché non lo fai e basta declare -ir false=0 true=1? Qual è il vantaggio di usare un array?
Benjamin W.

@BenjaminW. Volevo solo menzionare l' ropzione e il readonlycomando. Lo farei come mi hai suggerito nei miei script
sjsam

forse mi sono perso qualcosa ma perché non vero e falso dichiarato in questo modo usare il simbolo del dollaro? $ true $ false
qodeninja il

Letteralmente basta copiare la mia risposta e peggiorarla.
Quolonel Questions

@QuolonelQuestions Le variabili di Bash non vengono digitate , quindi non ha senso dirlo. declare and use boolean variablesPotremmo semplicemente, in più di un modo, imitare / supporre che una variabile abbia un tipo . Non l'ho visto menzionato da nessuna parte nella tua risposta.
sjsam,

10

Invece di fingere un booleano e lasciare una trappola per i futuri lettori, perché non usare 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

perché non numeri interi?
phil294

3
@Blauhirn perché gli interi vengono usati in modo diverso a seconda delle lingue. In alcune lingue 0costringe a falsee 1a true. Per quanto riguarda i codici di uscita del programma (che Bash utilizza storicamente) è 0per un risultato positivo o truee tutto il resto è negativo / errore o false.
Hubert Grzeskowiak,

7

POSIX (interfaccia del sistema operativo portatile)

Mi manca qui il punto chiave, che è la portabilità. Ecco perché la mia intestazione ha POSIX in sé.

In sostanza, tutte le risposte votate sono corrette, con l'eccezione che sono troppo specifiche per Bash .

Fondamentalmente, vorrei solo aggiungere ulteriori informazioni sulla portabilità.


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

    test "$var" = true && yourCodeIfTrue || yourCodeIfFalse

    Nota importante: non lo consiglio più perché è lentamente deprecato e è più difficile combinare più istruzioni.

  2. Immagina cosa significano quelle parole truee cosa falsesignificano per il guscio, provalo 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" ))

    La shell non può interpretarla diversamente da una stringa. Spero che ti stia venendo l'idea di quanto sia buono usando la parola chiave corretta senza virgolette .

    Ma nessuno lo ha detto nelle risposte precedenti.

  3. Cosa significa questo? Bene, diverse cose.

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

    • Dato che sono trattati come numeri, dovresti trattarli anche in questo modo, cioè se definisci una variabile, dì:

      var_bool=true
      echo "$var_bool"
       true

      puoi crearne un valore opposto con:

      var_bool=$(( 1 - $var_bool ))  # same as $(( ! $var_bool ))
      echo "$var_bool"
      1

    Come puoi vedere tu stesso, la shell stampa la truestringa per la prima volta che la usi, ma da allora funziona tutto tramite un numero che 0rappresenta trueo 1rappresenta false, rispettivamente.


Infine, cosa dovresti fare con tutte queste informazioni

  • In primo luogo, una buona abitudine sarebbe assegnare 0invece di true; 1invece difalse .

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

    if [ "$var_bool" -eq 0 ]; then
         yourCodeIfTrue
    else
         yourCodeIfFalse
    fi

6

Per quanto riguarda la sintassi, questa è una semplice metodologia che utilizzo (per esempio) per gestire in modo coerente e corretto la logica booleana:

# Tests
var=
var=''
var=""
var=0
var=1
var="abc"
var=abc

if [[ -n "${var}" ]] ; then
    echo 'true'
fi
if [[ -z "${var}" ]] ; then
    echo 'false'
fi

# Results
# var=        # false
# var=''      # false
# var=""      # false
# var=0       # true
# var=1       # true
# var="abc"   # true
# var=abc     # true

Se la variabile non viene mai dichiarata la risposta è: # false

Quindi, un modo semplice per impostare una variabile su true (usando questa metodologia di sintassi) sarebbe var=1,; viceversa, var=''.

Riferimento:

-n = Vero se la lunghezza della stringa var è diversa da zero.

-z = Vero se la lunghezza della stringa var è zero.


5

In molti linguaggi di programmazione, il tipo booleano è, o è implementato come, un sottotipo di numero intero, in cui truesi comporta come 1e falsesi comporta come 0:

Matematicamente , l'algebra booleana assomiglia all'intero modulo aritmetico 2. Pertanto, se una lingua non fornisce un 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

uomo 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".


5

Bill Parker sta per essere votato , perché le sue definizioni sono invertite dalla normale convenzione sul 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 tutto ciò che è diverso da zero è errore. Mi dispiace, non ho ancora la credibilità di strada per votare o rispondergli direttamente.

Bash raccomanda di usare le doppie parentesi ora come un'abitudine invece delle singole parentesi, e il collegamento 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 con 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 tra parentesi doppie, non emetterà una dichiarazione di errore, ma produrrà un valore errato (beh, nel 50% delle possibili permutazioni). Valuterà a [[0 -eq true]] = successo, ma anche a [[0 -eq false]] = successo, il che è sbagliato (hmmm .... che dire di quell'integrato che è un valore numerico?).

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

Vi sono altre permutazioni del condizionale che daranno anche risultati errati. Fondamentalmente, qualsiasi cosa (diversa dalla condizione di errore elencata sopra) che imposta una variabile su un valore numerico e la confronta con un valore reale / falso, oppure imposta una variabile con un valore reale / falso e la confronta con un valore numerico. Inoltre, tutto ciò che imposta una variabile su un valore true / false e fa un confronto usando -eq. Quindi evita i -eqconfronti booleani ed evita di usare valori numerici per i confronti booleani. Ecco un riepilogo delle permutazioni che daranno 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 ciò che funziona. Usa builtin vero / falso sia per il tuo confronto che per le tue valutazioni (come ha notato Mike Hunt, non racchiuderli tra virgolette). Quindi utilizzare il segno uguale o singolo o doppio (= o ==) e parentesi singole o doppie ([] 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 qui.


2
I true/ falsebuilt-in non sono usati qui (ignora ciò che l'evidenziazione della sintassi di alcuni editor potrebbe implicare), specialmente nei […]casi in cui puoi pensarlo come una semplice stringa qui (una che viene data come parametro al [comando).
phk,

Ce l'hai adesso
Peter Mortensen,

4

Le mie scoperte e i miei suggerimenti differiscono un po 'dagli altri post. Ho scoperto che avrei potuto usare i "booleani" sostanzialmente come si farebbe in qualsiasi linguaggio "normale", senza il "salto del cerchio" suggerito ...

Non c'è alcun bisogno []o confronto esplicito di stringhe ... Ho provato più distribuzioni Linux. Ho testato Bash, Dash e BusyBox . 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?

Se si imposta una variabile su true, successivamente viene valutata come "affermativa" all'interno di un condizionale. Impostalo su falsee viene valutato come "negativo". Molto semplice! L'unica avvertenza è che anche una variabile indefinita viene valutata come vera ! Sarebbe bello se facesse il contrario (come nella maggior parte delle lingue), ma questo è il trucco: devi solo inizializzare esplicitamente i tuoi booleani su vero o falso .

Perché funziona in questo modo? Questa risposta è duplice. A) vero / falso in una shell significa davvero "nessun errore" vs "errore" (ovvero 0 vs qualsiasi altra cosa). B) vero / falso non sono valori, ma piuttosto istruzioni nello scripting della shell! Per quanto riguarda il secondo punto, l'esecuzione trueo falsesu una riga da sola imposta il valore di ritorno per il blocco in cui ci si trova su quel valore, ovvero falseè una dichiarazione di "errore rilevato", dove "cancella" vero. Usandolo con un'assegnazione a una variabile "restituisce" quello nella variabile. Una variabile non definita viene valutata come truein un condizionale perché equivale a 0 o "nessun errore riscontrato".

Vedi le linee Bash di esempio e i 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

1

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

1

Ecco un'implementazione di una mano corta if true.

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

Sì, la decomposizione funzionale è sottovalutata.
Peter Mortensen,

1

Ho trovato confuse le risposte esistenti.

Personalmente, voglio solo avere qualcosa che assomigli 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 soddisfare tutti, ho testato:

snapshotEvents=false

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

Che ha anche funzionato bene.

La $snapshotEventsvaluta i contenuti del valore della variabile. Quindi hai bisogno del $.

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


2
Dove rimuovi le parentesi, questa è esattamente la risposta originale di @ miku in alto.
dolmen

1
Senza parentesi l'espressione non viene valutata.
sarà il

@sì sì. Non è necessario il () s.
phil294,

1
@Blauhirn ... Ciao, ho basato i miei commenti su esperimenti con GNU Bash su un PC Linux Mint / Ubuntu. Probabilmente hai ragione in teoria () : non sono necessari. La mia unica risposta, è provarlo, sembra dipendere dalla versione di Bash, dall'espressione o dal contesto attuali e simili.
sarà il

1

Ecco un miglioramento della risposta originale di Miku che affronta le preoccupazioni di Dennis Williamson 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 inviato a un programma.

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

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


1

Puoi usare shFlags .

Ti dà la possibilità di definire: DEFINE_bool

Esempio:

DEFINE_bool(big_menu, true, "Include 'advanced' options in the menu listing");

Dalla riga di comando è possibile definire:

sh script.sh --bigmenu
sh script.sh --nobigmenu # False

GFlags non ha senso in questa risposta: è una libreria C ++. Non può essere utilizzato direttamente negli script di shell.
Jonathan Cross,

Risposta aggiornata a shFlags che è una porta di GFlags per shell.
gogasca,

0

Questo è un test di velocità su diversi modi per testare i valori "booleani" in Bash:

#!/bin/bash
rounds=100000

b=true # For true; b=false for false
type -a true
time for i in $(seq $rounds); do command $b; done
time for i in $(seq $rounds); do $b; done
time for i in $(seq $rounds); do [ "$b" == true ]; done
time for i in $(seq $rounds); do test "$b" == true; done
time for i in $(seq $rounds); do [[ $b == true ]]; done

b=x; # Or any non-null string for true; b='' for false
time for i in $(seq $rounds); do [ "$b" ]; done
time for i in $(seq $rounds); do [[ $b ]]; done

b=1 # Or any non-zero integer for true; b=0 for false
time for i in $(seq $rounds); do ((b)); done

Stamperebbe qualcosa del genere

true is a shell builtin
true is /bin/true

real    0m0,815s
user    0m0,767s
sys     0m0,029s

real    0m0,562s
user    0m0,509s
sys     0m0,022s

real    0m0,829s
user    0m0,782s
sys     0m0,008s

real    0m0,782s
user    0m0,730s
sys     0m0,015s

real    0m0,402s
user    0m0,391s
sys     0m0,006s

real    0m0,668s
user    0m0,633s
sys     0m0,008s

real    0m0,344s
user    0m0,311s
sys     0m0,016s

real    0m0,367s
user    0m0,347s
sys     0m0,017s

-2

Alternativa: utilizzare una funzione

is_ok(){ :;}
is_ok(){ return 1;}
is_ok && echo "It's OK" || echo "Something's wrong"

Definire la funzione è meno intuitivo, ma controllare il suo valore di ritorno è molto semplice.


1
Questa non è una variabile che potresti testare, ma una funzione costante
jarno,

@jarno Testare il valore di ritorno di una funzione è diverso dal test di una variabile, ai fini di uno script?
johnraff,

Bene, la domanda riguarda le variabili.
jarno,

Vero, sebbene l'utilizzo in uno script di shell sarebbe lo stesso.
johnraff,

-2

Bash confonde davvero la questione con artisti del calibro di [, [[, ((, $((, etc.

Tutti calpestando gli spazi dei codici degli altri. Immagino che questo sia per lo più storico, in cui Bash doveva fingere di esserlo di shtanto in tanto.

Il più delle volte, posso semplicemente scegliere un metodo e attenermi ad esso. In questo caso, tendo a dichiarare (preferibilmente in un file di libreria comune che posso includere .nei miei script effettivi).

TRUE=1; FALSE=0

Posso quindi usare l' operatore ((... ))aritmetico per testare così.

testvar=$FALSE

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

if (( testvar == TRUE )); then
    # Do stuff because the directory does exist
fi
  1. Devi essere disciplinato. Il tuo testvarDEVE essere sia impostata su $TRUEo $FALSEin ogni momento.

  2. In ((... ))comparatori, non è necessario il precedente $, che lo rende più leggibile.

  3. Posso usare ((... ))perché $TRUE=1e $FALSE=0, cioè valori numerici.

  4. L'aspetto negativo è di dover usare $occasionalmente:

    testvar=$TRUE

    che non è così carino.

Non è una soluzione perfetta, ma copre tutti i casi di cui ho bisogno per un simile test.


2
Dovresti dichiarare le tue costanti in sola lettura. Inoltre, utilizzare sempre parentesi graffe quando si utilizzano le variabili. È una convenzione che tutti dovrebbero attenersi a IMHO. Il grande svantaggio di questa soluzione è che non è possibile mescolare l'espressione algebrica con flag di test o confronti di stringhe.
Hubert Grzeskowiak,
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.