Qual è la migliore pratica per rappresentare un valore booleano in uno script di shell?


15

So che ci sono valori booleani in bash, ma non li vedo mai usati da nessuna parte.

Voglio scrivere un wrapper per alcune informazioni cercate spesso sulla mia macchina, ad esempio, questa particolare unità USB è inserita / montata.

Quale sarebbe la migliore pratica per raggiungere questo obiettivo?

  • Una stringa?

    drive_xyz_available=true
  • Un numero (0 per vero, ≠ 0 per falso)?

    drive_xyz_available=0    # evaluates to true
  • Una funzione?

    drive_xyz_available() { 
        if available_magic; then 
                return 0 
        else 
                return 1 
        fi
    }

Per lo più mi chiedo cosa ci si aspetterebbe da altre persone che vorrebbero usare il wrapper. Si aspetterebbero un valore booleano, un comando come variabile o una funzione da chiamare?

Dal punto di vista della sicurezza, penso che la seconda opzione sia la più sicura, ma mi piacerebbe sentire le tue esperienze.


3
help true ; help false ; help exit
Costas,

3
@Costas Ti dispiacerebbe elaborare?
Minix,

Risposte:


2
bool(){ return "$((!${#1}))"; }

if bool "$var"
then : do true
else : do false

Basta impostare una variabile su qualsiasi cosa ma non nulla per far funzionare quanto sopra, anche se [ -n "$var" ]sarebbe più breve, se non altrettanto ovvio.

In generale, quando uno script interpreta una variabile di ambiente come vera o falsa, interpreterà qualsiasi valore per essere vero (e talvolta usa detto valore per configurare alcune opzioni) oppure un valore null come falso.

Quanto sopra restituisce il !notvalore booleano della len del suo primo argomento - se l'argomento contiene un numero qualsiasi di caratteri diversi da 0, restituisce 0, altrimenti, se non ci sono caratteri, restituisce 1. Questo è lo stesso test con cui puoi eseguire [ -n "$var" ], fondamentalmente , ma lo avvolge semplicemente in una piccola funzione denominata bool().

Questo è in genere il modo in cui funziona una variabile flag . Per esempio:

[ -d "$dir" ] || dir=

Laddove altre parti di uno script devono solo cercare qualsiasi valore $dirper valutarne l'utilità. Questo è utile anche per quanto riguarda la sostituzione dei parametri, poiché i parametri possono essere espansi ai valori predefiniti per riempire quelli vuoti o non impostati, ma altrimenti si espanderà a un valore predefinito come ...

for set in yes ''
do echo "${set:-unset or null}"
done

... che stamperebbe ...

yes
unset or null

Naturalmente, è anche possibile fare il contrario con, :+ma ciò può solo darti un valore predefinito predefinito o nulla, mentre il modulo sopra può darti un valore o un valore predefinito.

E così per quanto riguarda le tre scelte: ognuna potrebbe funzionare a seconda di come si sceglie di implementarla. Il ritorno della funzione è un test automatico, ma, se tale ritorno deve essere salvato per qualsiasi motivo, dovrà essere inserito in una variabile. Dipende dal caso d'uso: è il valore booleano che desideri valutare un test una volta e fatto? In tal caso, eseguire la funzione, altrimenti una delle altre due è probabilmente necessaria.


1
Non credo che tu stia rispondendo alla mia domanda. Non sto chiedendo come i booleani possano essere usati in uno script di shell, ma quale sia il modo più comune e che ci si aspetterebbe da un altro utente. Se la mia domanda non è chiara, sarei felice di modificarla. Anche una breve spiegazione di ciò che fa la tua risposta sarebbe buona. Grazie.
Minix,

@Minix Meglio?
Mikeserv,

In genere assegno trueo falsea una variabile, quindi puoi fareif $variable; then ...
wurtel

@wurtel - che si basa sull'impostazione predefinita $IFS- e se il valore del var potrebbe contenere input dell'utente di qualsiasi tipo, allora anche pura fortuna. Se il valore non è sconosciuto all'inizio, non è necessario testarlo. Più in sicurezza puoi fare if ${var:+":"} false; thenquando lavori con valori booleani null / non null. Ma questo è raramente più utile di[ -n "$var" ] &&
mikeserv il

Se inizio il mio script con variable=falsee poi lo imposta su true in base a qualsiasi condizione (proprio come farei quando si utilizza una variabile in C), allora non c'è alcun problema, qual è la tua ossessione per i valori IFS variabili e valori variabili casuali ecc. Comunque .. .
wurtel

4

In bashogni variabile è essenzialmente una stringa (o una matrice o una funzione, ma parliamo di variabili regolari qui).

Le condizioni vengono analizzate in base ai valori restituiti dai comandi di test: il valore restituito non è una variabile, è uno stato di uscita. Quando valuti if [ ... ]o if [[ ]]o if grep somethingqualcosa del genere, il valore restituito 0 (non la stringa 0, ma lo stato di uscita 0 = successo) significa vero e il resto significa falso (quindi esattamente il contrario di quello a cui sei abituato nei linguaggi di programmazione compilati, ma poiché esiste un modo per avere successo e molti modi per fallire e il risultato previsto dell'esecuzione è in genere un successo, 0 viene utilizzato come risultato predefinito più comune se nulla va storto). Questo è molto utile perché qualsiasi binario può essere usato come test - se fallisce, è falso, altrimenti è vero.

truee i falseprogrammi (di solito sovrascritti dai builtin) sono solo piccoli programmi utili che non fanno nulla: non trueriescono a fare nulla ed escono con 0, mentre falsetentano di non fare nulla e "falliscono", uscendo con 1. Sembra inutile ma è molto utile per gli script.

Quanto a come passare la veridicità in giro, dipende da te. È abbastanza comune usare solo "y" o "yes" per la verità e l'uso if [ x"$variable" = x"yes" ](ha aggiunto la stringa fittizia xperché se $variablesembra essere di lunghezza zero, questo protegge dalla creazione di un comando fasullo if [ = "yes" ]che non analizza). Potrebbe anche essere utile utilizzare semplicemente una stringa vuota per false e utilizzare [ -z "$variable ]per verificare se è di lunghezza zero (o -nse deve essere diversa da zero).

Ad ogni modo, è abbastanza raro dover effettivamente passare valori booleani in bash- è molto più comune semplicemente exitin caso di fallimento o restituire un risultato utile (o zero se qualcosa va storto e testare una stringa vuota), e la maggior parte dei casi può verifica guasti direttamente dallo stato di uscita.


Nel tuo caso, vuoi una funzione che fungerà da qualsiasi altro comando (quindi, restituisce 0 in caso di successo), quindi la tua ultima opzione sembra la scelta giusta.

Inoltre, potresti non aver nemmeno bisogno di returndichiarazioni. Se la funzione è abbastanza semplice, è possibile utilizzare il fatto che restituisce semplicemente lo stato dell'ultimo comando eseguito nella funzione. Quindi la tua funzione può essere semplicemente

drive_xyz_available() {
   [ -e /dev/disk/by-uuid/whatever ]
}

se si sta verificando l'esistenza di un nodo del dispositivo (o grep /proc/mountsper verificare se è montato?).


È un bel riassunto, grazie per aver dedicato del tempo a scriverlo. Posso dedurre dal tuo ultimo paragrafo che considereresti l'opzione di drive_xyz_available()essere la più comune?
Minix,

Potete fornire alcuni esempi per il y = truecaso comune ? Nella mia esperienza, la maggior parte degli script wrapper verifica qualsiasi valore non nullo per considerarlo vero - almeno per quanto riguarda le variabili d'ambiente interpretate. Altrimenti ignorano del tutto le macchine conchiglie.
Mikeserv,

3
La stringa fittizia per il iftest non è necessaria se la variabile è quotata; if [ "$variable" = "yes" ]funziona bene anche se $ variabile non è impostata.
Daniel Kullmann,

-2

Fondamentalmente qualsiasi numero diverso da 0 è vero e 0 è falso. Motivo per la restituzione di valori come 0 per la conclusione corretta di uno script o di qualsiasi altro numero per identificare diversi tipi di errori.

$? restituirà il codice di uscita del precedente comando / eseguibile, essendo 0 esito positivo e qualsiasi altro numero l'errore che è stato restituito.

Quindi userei quel metodo per vero / falso. if (( ! $? ));then OK;else NOOK;fi


Ne scriverò uno per l'ultima opzione, quindi. Grazie.
Minix,

5
Un numero diverso da zero in realtà è falso e zero è vero. Guarda l'output di true; echo $?e false; echo $?.
Ruslan,

@Ruslan. Hai controllato l'output del mio comando? Immagino di no, altrimenti non avresti dichiarato quello che hai fatto. 0 è falso, qualsiasi altro numero è vero, motivo per negare !il risultato affinché sia ​​VERO. Il risultato di un comando è 0 quando ha terminato correttamente, il che non significa che 0 sia vero. La parola truepuò essere 0, ma 0 non è mai vero come ifdimostra la condizione.
YoMismo,

È lo stesso che dire che in C / C ++ 0è trueperché if(!x){True();}else{False();}chiamerà True()quando x==0. Ma il controllo corretto non sarebbe !x, ma piuttosto !!x.
Ruslan,

Ti sbagli di grosso. Non sto parlando di cosa e quale ordine abbia qualunque cosa tu scriva dopo ifche dichiaro semplicemente il fatto che qui (in bash, o ksh, o tsh, o ....) come in C / C ++ uno 0 è FALSO, qualsiasi l'altro numero è TRUE, come puoi leggere nel link che segue, le implementazioni iniziali di C non hanno fornito alcun tipo booleano, essendo definite come ints dove 0 era FALSE e 1 TRUE en.wikipedia.org/wiki/Boolean_data_type .
YoMismo,
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.