Come verificare se una variabile è impostata in Bash?


1568

Come faccio a sapere se una variabile è impostata in Bash?

Ad esempio, come posso verificare se l'utente ha assegnato il primo parametro a una funzione?

function a {
    # if $1 is set ?
}

12
if test $# -gt 0; then printf 'arg <%s>\n' "$@"; fi.
Jens,

210
Nota per i cercatori di soluzioni: ci sono molte risposte molto apprezzate a questa domanda che rispondono alla domanda "è variabile non vuota". Le soluzioni di correzione più ("è set variabile") sono menzionate nelle risposte di Jens e Lionel di seguito.
Nathan Kidd,

8
Anche Russell Harmon e Seamus hanno ragione con il loro -vtest, sebbene questo sia apparentemente disponibile solo su nuove versioni bashe non portatile su shell.
Graeme,

5
Come sottolineato da @NathanKidd, Lionel e Jens forniscono soluzioni corrette. prosseek, dovresti passare la risposta accettata a una di queste.
Garrett,

3
... o la risposta errata potrebbe essere declassata dai più esigenti tra noi, poiché @prosseek non sta affrontando il problema.
dan3,

Risposte:


2305

(Di solito) Nel modo giusto

if [ -z ${var+x} ]; then echo "var is unset"; else echo "var is set to '$var'"; fi

dove ${var+x}è un parametro di espansione che non valuta nulla se non varè impostato, e sostituisce la stringa xaltrimenti.

Citazioni Digressione

Le virgolette possono essere omesse (quindi possiamo dire ${var+x}invece di "${var+x}") perché questa sintassi e l'utilizzo garantiscono che si espanderà solo a qualcosa che non richiede virgolette (poiché si espande in x(che non contiene interruzioni di parole quindi non ha bisogno di virgolette), oppure a niente (che risulta [ -z ], che valuta convenientemente lo stesso valore (vero) che [ -z "" ]fa altrettanto)).

Tuttavia, mentre le virgolette possono essere tranquillamente omesse e non è stato immediatamente ovvio per tutti (non era nemmeno evidente al primo autore di questa spiegazione delle citazioni che è anche un importante programmatore di Bash), a volte sarebbe meglio scrivere la soluzione con virgolette come [ -z "${var+x}" ], al costo molto piccolo possibile di una penalità di velocità O (1). Il primo autore ha anche aggiunto questo come commento accanto al codice usando questa soluzione che fornisce l'URL a questa risposta, che ora include anche la spiegazione del perché le citazioni possono essere omesse in modo sicuro.

(Spesso) Nel modo sbagliato

if [ -z "$var" ]; then echo "var is blank"; else echo "var is set to '$var'"; fi

Questo è spesso sbagliato perché non distingue tra una variabile non impostata e una variabile impostata sulla stringa vuota. Vale a dire, se var='', allora la soluzione sopra mostrerà "var is blank".

La distinzione tra unset e "set su stringa vuota" è essenziale nelle situazioni in cui l'utente deve specificare un'estensione o un elenco aggiuntivo di proprietà e che non specificandole per impostazione predefinita è un valore non vuoto, mentre la specifica della stringa vuota dovrebbe fare in modo che lo script utilizzi un'estensione vuota o un elenco di proprietà aggiuntive.

La distinzione potrebbe non essere essenziale in ogni scenario però. In quei casi [ -z "$var" ]andrà bene.


29
@Garrett, la tua modifica ha reso questa risposta errata, ${var+x}è la sostituzione corretta da usare. L'uso [ -z ${var:+x} ]non produce risultati diversi da [ -z "$var" ].
Graeme,

11
Questo non funziona Sto ottenendo "non impostato" indipendentemente dal fatto che var sia impostato su un valore o meno (cancellato con "unset var", "echo $ var" non produce alcun output).
Brent212,

10
Per la sintassi della soluzione $ {parametro + parola}, la sezione del manuale ufficiale è gnu.org/software/bash/manual/… ; tuttavia, un bug in questo, non menziona molto chiaramente questa sintassi ma dice solo la citazione (L'omissione dei due punti [":"] comporta un test solo per un parametro non impostato. $ $ parametro: + parola} [significa] Se il parametro è nullo o non impostato, non viene sostituito nulla, altrimenti viene sostituita l'espansione della parola.); il riferimento citato pubs.opengroup.org/onlinepubs/9699919799/utilities/… (buono a sapersi) ha documenti molto più chiari qui.
Destiny Architect,

7
Sembra che le virgolette siano necessarie quando si usano espressioni multiple, ad esempio [ -z "" -a -z ${var+x} ]ottiene bash: [: argument expectedin bash 4.3-ubuntu (ma non per esempio zsh). Non mi piace molto la risposta usando una sintassi che sembra funzionare in alcuni casi.
FauxFaux,

41
Usare un semplice [ -z $var ]non è più "sbagliato" che omettere le virgolette. Ad ogni modo, stai facendo ipotesi sul tuo contributo. Se stai bene trattando una stringa vuota come non impostata, [ -z $var ]è tutto ciò di cui hai bisogno.
n.

910

Per verificare la variabile stringa non nulla / non zero, ovvero se impostato, utilizzare

if [ -n "$1" ]

È l'opposto di -z. Mi trovo a usare -npiù di -z.

Lo useresti come:

if [ -n "$1" ]; then
  echo "You supplied the first parameter!"
else
  echo "First parameter not supplied."
fi

86
Io di solito preferisco [[ ]]sopra [ ], come [[ ]]è più potente e provoca meno problemi in determinate situazioni (vedi questa domanda per una spiegazione della differenza tra i due). La domanda richiede specificamente una soluzione bash e non menziona alcun requisito di portabilità.
Flusso

18
La domanda è come si può vedere se è impostata una variabile . Quindi non puoi presumere che la variabile sia impostata.
Ciao Arrivederci,

7
Sono d'accordo, []funziona meglio soprattutto per gli script shell (non bash).
Hengjie,


63
Nota che -nè il test predefinito, quindi più semplicemente [ "$1" ]o [[ $1 ]]funziona pure. Si noti inoltre che con la [[ ]] citazione non è necessario .
TNE

478

Ecco come verificare se un parametro non è impostato o vuoto ("Null") o impostato con un valore :

+--------------------+----------------------+-----------------+-----------------+
|   Expression       |       parameter      |     parameter   |    parameter    |
|   in script:       |   Set and Not Null   |   Set But Null  |      Unset      |
+--------------------+----------------------+-----------------+-----------------+
| ${parameter:-word} | substitute parameter | substitute word | substitute word |
| ${parameter-word}  | substitute parameter | substitute null | substitute word |
| ${parameter:=word} | substitute parameter | assign word     | assign word     |
| ${parameter=word}  | substitute parameter | substitute null | assign word     |
| ${parameter:?word} | substitute parameter | error, exit     | error, exit     |
| ${parameter?word}  | substitute parameter | substitute null | error, exit     |
| ${parameter:+word} | substitute word      | substitute null | substitute null |
| ${parameter+word}  | substitute word      | substitute word | substitute null |
+--------------------+----------------------+-----------------+-----------------+

Fonte: POSIX: Espansione parametri :

In tutti i casi indicati con "sostituto", l'espressione viene sostituita con il valore visualizzato. In tutti i casi indicati con "Assegna", al parametro viene assegnato quel valore, che sostituisce anche l'espressione.

Per mostrarlo in azione:

+--------------------+----------------------+-----------------+-----------------+
|   Expression       |  FOO="world"         |     FOO=""      |    unset FOO    |
|   in script:       |  (Set and Not Null)  |  (Set But Null) |     (Unset)     |
+--------------------+----------------------+-----------------+-----------------+
| ${FOO:-hello}      | world                | hello           | hello           |
| ${FOO-hello}       | world                | ""              | hello           |
| ${FOO:=hello}      | world                | FOO=hello       | FOO=hello       |
| ${FOO=hello}       | world                | ""              | FOO=hello       |
| ${FOO:?hello}      | world                | error, exit     | error, exit     |
| ${FOO?hello}       | world                | ""              | error, exit     |
| ${FOO:+hello}      | hello                | ""              | ""              |
| ${FOO+hello}       | hello                | hello           | ""              |
+--------------------+----------------------+-----------------+-----------------+

5
@HelloGoodbye Sì, funziona: set foo; echo ${1:-set but null or unset}echos "foo"; set --; echo ${1:-set but null or unset}echi impostati ma nulli ...
Jens,

4
@HelloGoodbye I parametri posizionali possono essere impostati con uh set:-)
Jens,

95
Questa risposta è molto confusa. Qualche esempio pratico su come usare questa tabella?
Ben Davis,

12
@BenDavis I dettagli sono spiegati nel collegamento allo standard POSIX, che fa riferimento al capitolo da cui è stata tratta la tabella. Un costrutto che uso in quasi tutti gli script che scrivo è : ${FOOBAR:=/etc/foobar.conf}quello di impostare un valore predefinito per una variabile se non è impostato o nullo.
Jens,

1
parameterè qualsiasi nome di variabile. wordè una stringa da sostituire a seconda della sintassi nella tabella che si sta utilizzando e se la variabile $parameterè impostata, impostata ma nulla o non impostata. Non per rendere le cose più complicate, ma wordpuò anche includere variabili! Questo può essere molto utile per passare argomenti facoltativi separati da un carattere. Ad esempio: ${parameter:+,${parameter}}genera una virgola separata ,$parameterse è impostata ma non nulla. In questo caso, wordè,${parameter}
TrinitronX il

260

Mentre la maggior parte delle tecniche qui indicate sono corrette, bash 4.2 supporta un test effettivo per la presenza di una variabile ( man bash ), piuttosto che testare il valore della variabile.

[[ -v foo ]]; echo $?
# 1

foo=bar
[[ -v foo ]]; echo $?
# 0

foo=""
[[ -v foo ]]; echo $?
# 0

In particolare, questo approccio non causerà un errore se utilizzato per verificare la presenza di una variabile non impostata in set -u/ set -o nounsetmode, a differenza di molti altri approcci, come l'utilizzo [ -z.


9
In bash 4.1.2, indipendentemente dal fatto che la variabile sia impostata, [[ -v aaa ]]; echo $?==>-bash: conditional binary operator expected -bash: syntax error near 'aaa'
Dan

32
L'argomento '-v' al builtin 'test' è stato aggiunto in bash 4.2.
Ti Strga,

19
l'argomento -v è stato aggiunto come complemento all'opzione -u shell (nounset). Con 'nounset' attivato (set -u), la shell restituirà un errore e terminerà, se non è interattiva. $ # era buono per controllare i parametri posizionali. Tuttavia, le variabili denominate necessitavano in qualche altro modo, diverso dall'ostruzione, per identificarle come non impostate. È un peccato che questa funzionalità sia arrivata così tardi perché molti sono tenuti a essere compatibili con le versioni precedenti, nel qual caso non possono usarla. L'unica altra opzione è quella di usare trucchi o "hack" per aggirarlo come mostrato sopra, ma è il più inelegante.
Osirisgothra,

2
Nota che questo non funziona per le variabili dichiarate ma non impostate. ad es.declare -a foo
miken32,

17
Questo è il motivo per cui continuo a leggere dopo la risposta accettata / più votata.
Joe,

176

Esistono molti modi per farlo con uno dei seguenti:

if [ -z "$1" ]

Ciò ha esito positivo se $ 1 è nullo o non impostato


46
C'è una differenza tra un parametro non impostato e un parametro con un valore null.
Chepner,

21
Voglio solo essere pedante e sottolineare che questa è la risposta opposta a ciò che pone la domanda. Le domande chiedono se la variabile è impostata, non se la variabile non è impostata.
Philluminati,

47
[[ ]]non è altro che una trappola della portabilità. if [ -n "$1" ]dovrebbe essere usato qui.
Jens,

73
Questa risposta non è corretta La domanda chiede un modo per stabilire se è impostata una variabile, non se è impostata su un valore non vuoto. Dato foo="", $fooha un valore, ma -zsegnalerà semplicemente che è vuoto. E -z $fooesploderà se hai set -o nounset.
Keith Thompson,

4
Dipende dalla definizione di "variabile impostata". Se si desidera verificare se la variabile è impostata su una stringa diversa da zero , la risposta di mbranning è corretta. Ma se vuoi verificare se la variabile è dichiarata ma non inizializzata (cioè foo=), la risposta di Russel è corretta.
Flusso

77

Trovo sempre la tabella POSIX nell'altra risposta lenta a grok, quindi ecco la mia opinione su di essa:

   +----------------------+------------+-----------------------+-----------------------+
   |   if VARIABLE is:    |    set     |         empty         |        unset          |
   +----------------------+------------+-----------------------+-----------------------+
 - |  ${VARIABLE-default} | $VARIABLE  |          ""           |       "default"       |
 = |  ${VARIABLE=default} | $VARIABLE  |          ""           | $(VARIABLE="default") |
 ? |  ${VARIABLE?default} | $VARIABLE  |          ""           |       exit 127        |
 + |  ${VARIABLE+default} | "default"  |       "default"       |          ""           |
   +----------------------+------------+-----------------------+-----------------------+
:- | ${VARIABLE:-default} | $VARIABLE  |       "default"       |       "default"       |
:= | ${VARIABLE:=default} | $VARIABLE  | $(VARIABLE="default") | $(VARIABLE="default") |
:? | ${VARIABLE:?default} | $VARIABLE  |       exit 127        |       exit 127        |
:+ | ${VARIABLE:+default} | "default"  |          ""           |          ""           |
   +----------------------+------------+-----------------------+-----------------------+

Si noti che ciascun gruppo (con e senza precedente colon) ha lo stesso set e unset casi, quindi l'unica cosa che differisce è come i vuoti casi sono trattati.

Con i due punti precedenti, i casi vuoti e non impostati sono identici, quindi userei quelli dove possibile (cioè uso :=, non solo =, perché il caso vuoto è incoerente).

Titoli:

  • set significa VARIABLEnon è vuoto ( VARIABLE="something")
  • vuoto significa VARIABLEè vuoto / null ( VARIABLE="")
  • unset significa VARIABLEche non esiste ( unset VARIABLE)

Valori:

  • $VARIABLE significa che il risultato è il valore originale della variabile.
  • "default" significa che il risultato è stato la stringa di sostituzione fornita.
  • "" significa che il risultato è null (una stringa vuota).
  • exit 127 significa che lo script termina l'esecuzione con il codice di uscita 127.
  • $(VARIABLE="default")significa che il risultato è il valore originale della variabile e la stringa di sostituzione fornita viene assegnata alla variabile per un uso futuro.

1
Hai corretto solo quelli che sono veri e propri errori di codifica (istanze di indiretta indesiderata quando si lavora con le variabili). Ho apportato una modifica per correggere alcuni altri casi in cui il dollaro fa la differenza nell'interpretazione della descrizione. A proposito, tutte le variabili shell (ad eccezione degli array) sono variabili stringa; può capitare che contengano una stringa che può essere interpretata come un numero. Parlare di stringhe annebbia solo il messaggio. Le citazioni non hanno nulla a che fare con le stringhe, sono solo un'alternativa alla fuga. VARIABLE=""potrebbe essere scritto come VARIABLE=. Tuttavia, il primo è più leggibile.
Palec,

Grazie per la tabella è molto utile, tuttavia sto cercando di far uscire lo script se non impostato o vuoto. Ma il codice di uscita che ottengo è 1 non 127.
Astronauta

64

Per vedere se una variabile non è vuota, io uso

if [[ $var ]]; then ...       # `$var' expands to a nonempty string

Il contrario verifica se una variabile non è impostata o è vuota:

if [[ ! $var ]]; then ...     # `$var' expands to the empty string (set or not)

Per vedere se una variabile è impostata (vuota o non vuota), io uso

if [[ ${var+x} ]]; then ...   # `var' exists (empty or nonempty)
if [[ ${1+x} ]]; then ...     # Parameter 1 exists (empty or nonempty)

Il contrario verifica se una variabile non è impostata:

if [[ ! ${var+x} ]]; then ... # `var' is not set at all
if [[ ! ${1+x} ]]; then ...   # We were called with no arguments

Anch'io lo uso da un po 'di tempo; solo in modo ridotto:[ $isMac ] && param="--enable-shared"

@Bhaskar Penso che sia perché questa risposta ha già fornito essenzialmente la stessa risposta (usare ${variable+x}per ottenere xiff $variableè impostata) quasi mezzo anno prima. Inoltre ha molti più dettagli che spiegano perché è giusto.
Mark Haferkamp,

ma ${variable+x}è meno leggibile (e ovvio)
knocte

35

Su una versione moderna di Bash (4.2 o successive penso; non lo so per certo), proverei questo:

if [ ! -v SOMEVARIABLE ] #note the lack of a $ sigil
then
    echo "Variable is unset"
elif [ -z "$SOMEVARIABLE" ]
then
    echo "Variable is set to an empty string"
else
    echo "Variable is set to some string"
fi

5
Si noti inoltre che [ -v "$VARNAME" ]non è errato, ma esegue semplicemente un test diverso. Supponiamo VARNAME=foo; quindi controlla se fooè stata impostata una variabile denominata .
Chepner,

5
Nota per chi desidera la portabilità, -vnon è conforme POSIX
kzh

Se si ottiene [: -v: unexpected operator, è necessario assicurarsi di utilizzare bashinvece di sh.
Koppor,

25
if [ "$1" != "" ]; then
  echo \$1 is set
else
  echo \$1 is not set
fi

Anche se per argomenti è normalmente meglio testare $ #, che è il numero di argomenti, secondo me.

if [ $# -gt 0 ]; then
  echo \$1 is set
else
  echo \$1 is not set
fi

1
Il primo test è al contrario; Penso che tu voglia [ "$1" != "" ](o [ -n "$1" ]) ...
Gordon Davisson il

10
Ciò fallirà se $1è impostato sulla stringa vuota.
Ciao Arrivederci,

2
La seconda parte della risposta (conteggio dei parametri) è una soluzione utile per la prima parte della risposta che non funziona quando $1è impostata su una stringa vuota.
Mark Berry,

Questo fallirà se set -o nounsetè attivo.
Flimm,

21

Nota

Sto dando una risposta fortemente incentrata su Bash a causa del bashtag.

Risposta breve

Finché hai a che fare solo con variabili nominate in Bash, questa funzione dovrebbe sempre dirti se la variabile è stata impostata, anche se è un array vuoto.

variable-is-set() {
    declare -p "$1" &>/dev/null
}

Perché questo funziona

In Bash (almeno fino alla 3.0), se varè una variabile dichiarata / impostata, quindi declare -p vargenera un declarecomando che imposta la variabile varsu qualunque sia il suo tipo e valore attuali e restituisce il codice di stato 0(successo). Se varnon dichiarato, declare -p vargenera un messaggio di errore stderre restituisce il codice di stato 1. Utilizzando &>/dev/null, reindirizza sia il normale stdoutche l' stderroutput a/dev/null , mai visto, e senza cambiare il codice di stato. Pertanto la funzione restituisce solo il codice di stato.

Perché altri metodi (a volte) falliscono in Bash

  • [ -n "$var" ]: Controlla solo se${var[0]} è vuoto. (In Bash, $varè lo stesso di ${var[0]}.)
  • [ -n "${var+x}" ]: Controlla solo se ${var[0]}è impostato.
  • [ "${#var[@]}" != 0 ]: Controlla solo se $varè impostato almeno un indice di .

Quando questo metodo fallisce in Bash

Questo funziona solo per le variabili di nome (compreso $_), non alcune variabili speciali ( $!, $@, $#, $$, $*, $?, $-, $0, $1, $2, ..., e qualsiasi che può aver dimenticato). Poiché nessuno di questi sono array, lo stile POSIX [ -n "${var+x}" ]funziona per tutte queste variabili speciali. Ma attenzione a racchiuderlo in una funzione poiché molte variabili speciali cambiano valori / esistenza quando vengono chiamate funzioni.

Nota sulla compatibilità della shell

Se il tuo script ha array e stai provando a renderlo compatibile con il maggior numero possibile di shell, allora considera l'utilizzo typeset -pinvece di declare -p. Ho letto che ksh supporta solo il primo, ma non sono stato in grado di testarlo. So che Bash 3.0+ e Zsh 5.5.1 supportano entrambitypeset -p e declare -p, differendo solo per il fatto che uno è un'alternativa all'altro. Ma non ho testato differenze oltre quelle due parole chiave e non ho testato altre shell.

Se è necessario che lo script sia compatibile con POSIX sh, non è possibile utilizzare le matrici. Senza array,[ -n "{$var+x}" ] funziona.

Codice di confronto per diversi metodi in Bash

Questa funzione disinserisce la variabile var, evals il codice passato, esegue i test per determinare se varè impostato daeval codice d e infine mostra i codici di stato risultanti per i diversi test.

Sto saltando test -v var, [ -v var ]e [[ -v var ]]perché producono risultati identici allo standard POSIX [ -n "${var+x}" ], pur richiedendo Bash 4.2+. Salto anche typeset -pperché è lo stesso declare -pdelle shell che ho testato (da Bash 3.0 a 5.0 e Zsh 5.5.1).

is-var-set-after() {
    # Set var by passed expression.
    unset var
    eval "$1"

    # Run the tests, in increasing order of accuracy.
    [ -n "$var" ] # (index 0 of) var is nonempty
    nonempty=$?
    [ -n "${var+x}" ] # (index 0 of) var is set, maybe empty
    plus=$?
    [ "${#var[@]}" != 0 ] # var has at least one index set, maybe empty
    count=$?
    declare -p var &>/dev/null # var has been declared (any type)
    declared=$?

    # Show test results.
    printf '%30s: %2s %2s %2s %2s\n' "$1" $nonempty $plus $count $declared
}

Codice del test case

Si noti che i risultati del test potrebbero essere imprevisti a causa del fatto che Bash tratta gli indici di array non numerici come "0" se la variabile non è stata dichiarata come array associativo. Inoltre, gli array associativi sono validi solo in Bash 4.0+.

# Header.
printf '%30s: %2s %2s %2s %2s\n' "test" '-n' '+x' '#@' '-p'
# First 5 tests: Equivalent to setting 'var=foo' because index 0 of an
# indexed array is also the nonindexed value, and non-numerical
# indices in an array not declared as associative are the same as
# index 0.
is-var-set-after "var=foo"                        #  0  0  0  0
is-var-set-after "var=(foo)"                      #  0  0  0  0
is-var-set-after "var=([0]=foo)"                  #  0  0  0  0
is-var-set-after "var=([x]=foo)"                  #  0  0  0  0
is-var-set-after "var=([y]=bar [x]=foo)"          #  0  0  0  0
# '[ -n "$var" ]' fails when var is empty.
is-var-set-after "var=''"                         #  1  0  0  0
is-var-set-after "var=([0]='')"                   #  1  0  0  0
# Indices other than 0 are not detected by '[ -n "$var" ]' or by
# '[ -n "${var+x}" ]'.
is-var-set-after "var=([1]='')"                   #  1  1  0  0
is-var-set-after "var=([1]=foo)"                  #  1  1  0  0
is-var-set-after "declare -A var; var=([x]=foo)"  #  1  1  0  0
# Empty arrays are only detected by 'declare -p'.
is-var-set-after "var=()"                         #  1  1  1  0
is-var-set-after "declare -a var"                 #  1  1  1  0
is-var-set-after "declare -A var"                 #  1  1  1  0
# If 'var' is unset, then it even fails the 'declare -p var' test.
is-var-set-after "unset var"                      #  1  1  1  1

Uscita di prova

La mnemonica test nella riga di intestazione corrispondono a [ -n "$var" ], [ -n "${var+x}" ], [ "${#var[@]}" != 0 ], e declare -p var, rispettivamente.

                         test: -n +x #@ -p
                      var=foo:  0  0  0  0
                    var=(foo):  0  0  0  0
                var=([0]=foo):  0  0  0  0
                var=([x]=foo):  0  0  0  0
        var=([y]=bar [x]=foo):  0  0  0  0
                       var='':  1  0  0  0
                 var=([0]=''):  1  0  0  0
                 var=([1]=''):  1  1  0  0
                var=([1]=foo):  1  1  0  0
declare -A var; var=([x]=foo):  1  1  0  0
                       var=():  1  1  1  0
               declare -a var:  1  1  1  0
               declare -A var:  1  1  1  0
                    unset var:  1  1  1  1

Sommario

  • declare -p var &>/dev/null è (100%?) affidabile per il test di variabili nominate in Bash da almeno 3.0.
  • [ -n "${var+x}" ] è affidabile in situazioni conformi a POSIX, ma non è in grado di gestire array.
  • Esistono altri test per verificare se una variabile non è vuota e per verificare le variabili dichiarate in altre shell. Ma questi test non sono adatti né per gli script Bash né POSIX.

3
Delle risposte che ho provato, questa ( declare -p var &>/dev/null) è l'UNICA che funziona. GRAZIE!
user1387866

2
@ mark-haferkamp Ho provato a modificare la tua risposta per aggiungere il precedente "/" critico al tuo codice funzione in modo che legga ... &> /dev/nullma SO non mi permetterebbe di modificare un singolo carattere 🙄 (deve essere> 6 caratteri - anche provato ad aggiungere spazi bianchi ma non ha funzionato). Inoltre, potrebbe valere la pena cambiare la funzione per passare $1a un campione chiamato variabile (ad es. OUTPUT_DIR) Poiché, come hai sottolineato, variabili speciali come $1non funzionano qui. Ad ogni modo, è una modifica critica, altrimenti il ​​codice sta cercando di creare un file chiamato nullin una relativa directory chiamata dev. A proposito, ottima risposta!
joehanna,

Inoltre, usare il nome della variabile senza il prefisso $ funziona anche: declare -p PATH &> /dev/nullrestituisce 0 dove come declare -p ASDFASDGFASKDF &> /dev/nullrestituisce 1. (su bash 5.0.11 (1) -release)
joehanna,

1
Ottimo lavoro! 1 avvertenza: declare vardichiara var una variabile ma non la imposta in termini di set -o nounset: prova condeclare foo bar=; set -o nounset; echo $bar; echo $foo
xebeche

@joehanna Grazie per aver colto quella mancanza /! L'ho risolto, ma penso che la logica sia abbastanza confusa da giustificare una funzione, specialmente alla luce del commento di @ xebeche. @xebeche Questo è scomodo per la mia risposta ... Sembra che dovrò provare qualcosa del genere declare -p "$1" | grep =o test -v "$1".
Mark Haferkamp,

19

Per coloro che stanno cercando di verificare che non siano impostati o vuoti quando si trovano in uno script con set -u:

if [ -z "${var-}" ]; then
   echo "Must provide var environment variable. Exiting...."
   exit 1
fi

Il [ -z "$var" ]controllo regolare avrà esito negativo con var; unbound variableif set -uma si [ -z "${var-}" ] espande in una stringa vuota se varnon viene impostato senza errore.


Ti manca un colon. Dovrebbe essere ${var:-}, no ${var-}. Ma in Bash, posso confermare che funziona anche senza i due punti.
Palec,

3
${var:-}e ${var-}significano cose diverse. ${var:-}si espanderà in stringa vuota se varnon impostato o null e ${var-}si espanderà in stringa vuota solo se non impostato.
RubenLaguna,

Ho appena imparato qualcosa di nuovo, grazie! Se si omettono i due punti si ottiene un test solo per un parametro non impostato. Detto in altro modo, se i due punti sono inclusi, l'operatore verifica l'esistenza di entrambi i parametri e che il suo valore non è nullo; se i due punti vengono omessi, l'operatore verifica solo l'esistenza. Qui non fa differenza, ma è buono a sapersi.
Palec,

17

Vuoi uscire se non è impostato

Questo ha funzionato per me. Volevo che il mio script uscisse con un messaggio di errore se non era stato impostato un parametro.

#!/usr/bin/env bash

set -o errexit

# Get the value and empty validation check all in one
VER="${1:?You must pass a version of the format 0.0.0 as the only argument}"

Questo ritorna con un errore quando viene eseguito

peek@peek:~$ ./setver.sh
./setver.sh: line 13: 1: You must pass a version of the format 0.0.0 as the only argument

Controlla solo, nessuna uscita - Vuoto e Non impostato sono NON VALIDI

Prova questa opzione se vuoi solo verificare se il valore impostato = VALID o unset / empty = INVALID.

TSET="good val"
TEMPTY=""
unset TUNSET

if [ "${TSET:-}" ]; then echo "VALID"; else echo "INVALID";fi
# VALID
if [ "${TEMPTY:-}" ]; then echo "VALID"; else echo "INVALID";fi
# INVALID
if [ "${TUNSET:-}" ]; then echo "VALID"; else echo "INVALID";fi
# INVALID

Oppure, anche brevi test ;-)

[ "${TSET:-}"   ] && echo "VALID" || echo "INVALID"
[ "${TEMPTY:-}" ] && echo "VALID" || echo "INVALID"
[ "${TUNSET:-}" ] && echo "VALID" || echo "INVALID"

Controlla solo, nessuna uscita - Solo vuoto è INVALID

E questa è la risposta alla domanda. Utilizzare questo se si desidera solo verificare se il valore impostato / vuoto = VALID o unset = INVALID.

NOTA, "1" in "..- 1}" è insignificante, può essere qualsiasi cosa (come x)

TSET="good val"
TEMPTY=""
unset TUNSET

if [ "${TSET+1}" ]; then echo "VALID"; else echo "INVALID";fi
# VALID
if [ "${TEMPTY+1}" ]; then echo "VALID"; else echo "INVALID";fi
# VALID
if [ "${TUNSET+1}" ]; then echo "VALID"; else echo "INVALID";fi
# INVALID

Brevi test

[ "${TSET+1}"   ] && echo "VALID" || echo "INVALID"
[ "${TEMPTY+1}" ] && echo "VALID" || echo "INVALID"
[ "${TUNSET+1}" ] && echo "VALID" || echo "INVALID"

Dedico questa risposta a @ mklement0 (commenti) che mi ha sfidato a rispondere con precisione alla domanda.

Riferimento http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_02


15

Per verificare se una variabile è impostata con un valore non vuoto, utilizzare [ -n "$x" ], come altri hanno già indicato.

Il più delle volte, è una buona idea trattare una variabile che ha un valore vuoto allo stesso modo di una variabile non impostata. Ma puoi distinguere i due se hai bisogno di: [ -n "${x+set}" ](si "${x+set}"espande in setif xè impostato e nella stringa vuota se xnon è impostato).

Per verificare se un parametro è stato passato, prova $#, che è il numero di parametri passati alla funzione (o allo script, quando non è in una funzione) (vedi la risposta di Paul ).


14

Leggi la sezione "Espansione dei parametri" della bashpagina man. L'espansione dei parametri non fornisce un test generale per una variabile impostata, ma ci sono diverse cose che puoi fare a un parametro se non è impostato.

Per esempio:

function a {
    first_arg=${1-foo}
    # rest of the function
}

verrà impostato first_arguguale a $1se assegnato, altrimenti utilizza il valore "pippo". Se aassolutamente deve accettare un singolo parametro e non esiste alcun valore predefinito valido, è possibile uscire con un messaggio di errore quando non viene fornito alcun parametro:

function a {
    : ${1?a must take a single argument}
    # rest of the function
}

(Nota l'uso di :come comando null, che espande semplicemente i valori dei suoi argomenti. In $1questo esempio non vogliamo fare nulla , basta uscire se non è impostato)


1
Sono sconcertato dal fatto che nessuna delle altre risposte menzioni il semplice ed elegante : ${var?message}.
Tripleee,

Da dove l'hai preso? Meraviglioso! Funziona! Ed è corto ed elegante! Grazie!
Roger,

13

In bash puoi usare -vall'interno del [[ ]]builtin:

#! /bin/bash -u

if [[ ! -v SOMEVAR ]]; then
    SOMEVAR='hello'
fi

echo $SOMEVAR

8

L'utilizzo [[ -z "$var" ]]è il modo più semplice per sapere se è stata impostata o meno una variabile, ma tale opzione -znon distingue tra una variabile non impostata e una variabile impostata su una stringa vuota:

$ set=''
$ [[ -z "$set" ]] && echo "Set" || echo "Unset" 
Unset
$ [[ -z "$unset" ]] && echo "Set" || echo "Unset"
Unset

È meglio verificarlo in base al tipo di variabile: variabile env, parametro o variabile normale.

Per una variabile env:

[[ $(env | grep "varname=" | wc -l) -eq 1 ]] && echo "Set" || echo "Unset"

Per un parametro (ad esempio, per verificare l'esistenza del parametro $5):

[[ $# -ge 5 ]] && echo "Set" || echo "Unset"

Per una variabile regolare (utilizzando una funzione ausiliaria, per farlo in modo elegante):

function declare_var {
   declare -p "$1" &> /dev/null
}
declare_var "var_name" && echo "Set" || echo "Unset"

Appunti:

  • $#: ti dà il numero di parametri posizionali.
  • declare -p: ti dà la definizione della variabile passata come parametro. Se esiste, restituisce 0, in caso contrario, restituisce 1 e stampa un messaggio di errore.
  • &> /dev/null: elimina l'output declare -psenza influire sul suo codice di ritorno.

5

Le risposte sopra non funzionano quando l'opzione Bash set -uè abilitata. Inoltre, non sono dinamici, ad esempio, come testare una variabile con il nome "fittizio" è definito? Prova questo:

is_var_defined()
{
    if [ $# -ne 1 ]
    then
        echo "Expected exactly one argument: variable name as string, e.g., 'my_var'"
        exit 1
    fi
    # Tricky.  Since Bash option 'set -u' may be enabled, we cannot directly test if a variable
    # is defined with this construct: [ ! -z "$var" ].  Instead, we must use default value
    # substitution with this construct: [ ! -z "${var:-}" ].  Normally, a default value follows the
    # operator ':-', but here we leave it blank for empty (null) string.  Finally, we need to
    # substitute the text from $1 as 'var'.  This is not allowed directly in Bash with this
    # construct: [ ! -z "${$1:-}" ].  We need to use indirection with eval operator.
    # Example: $1="var"
    # Expansion for eval operator: "[ ! -z \${$1:-} ]" -> "[ ! -z \${var:-} ]"
    # Code  execute: [ ! -z ${var:-} ]
    eval "[ ! -z \${$1:-} ]"
    return $?  # Pedantic.
}

Correlati: In Bash, come testare se una variabile è definita in modalità "-u"


1
Mi piacerebbe sapere così tanto perché questa risposta è stata sottoposta a voto negativo ... Funziona benissimo per me.
LavaScornedOven

In Bash, si potrebbe fare lo stesso senza un eval: il cambiamento eval "[ ! -z \${$1:-} ]"di valutazione indiretta: [ ! -z ${!1:-} ];.

@BinaryZebra: idea interessante. Ti suggerisco di pubblicare una risposta separata.
kevinarpe,

L'uso di "eval" rende questa soluzione vulnerabile all'iniezione di codice: $ is_var_defined 'x}]; echo "gotcha"> & 2; # 'gotcha
tetsujin,


4

Il mio modo preferito è questo:

$ var=10
$ if ! ${var+false};then echo "is set";else echo "NOT set";fi
is set
$ unset -v var
$ if ! ${var+false};then echo "is set";else echo "NOT set";fi
NOT set

Quindi sostanzialmente, se viene impostata una variabile, diventa "una negazione del risultante false" (ciò che sarà true= "è impostato").

E, se non è impostato, diventerà "una negazione del risultato true" (come il risultato vuoto indica true) (così finirà come false= "NON impostato").



1

In una shell è possibile utilizzare l' -zoperatore True se la lunghezza della stringa è zero.

Un semplice one-liner per impostare le impostazioni predefinite MY_VARse non è impostato, altrimenti è possibile visualizzare il messaggio:

[[ -z "$MY_VAR" ]] && MY_VAR="default"
[[ -z "$MY_VAR" ]] && MY_VAR="default" || echo "Variable already set."

0
if [[ ${1:+isset} ]]
then echo "It was set and not null." >&2
else echo "It was not set or it was null." >&2
fi

if [[ ${1+isset} ]]
then echo "It was set but might be null." >&2
else echo "It was was not set." >&2
fi

$1, $2E fino a $Nè sempre impostato. Siamo spiacenti, questo è fallito.

L'ho provato e non ha funzionato, forse la mia versione bash non ha supporto per quello. Idk, scusa se sbaglio. :)

0

Ho trovato un codice (molto) migliore per farlo se vuoi controllare qualcosa dentro $@.

if [[$ 1 = ""]]
poi
  echo '$ 1 è vuoto'
altro
  echo "$ 1 è pieno"
fi

Perché tutto questo? Tutto $@esiste in Bash, ma per impostazione predefinita è vuoto, quindi test -ze test -nnon potrebbe aiutarti.

Aggiornamento: puoi anche contare il numero di caratteri in un parametro.

se [$ {# 1} = 0]
poi
  echo '$ 1 è vuoto'
altro
  echo "$ 1 è pieno"
fi

Lo so, ma è tutto. Se qualcosa è vuoto, otterrai un errore! Scusate. : P

0
if [[ ${!xx[@]} ]] ; then echo xx is defined; fi

11
Benvenuto in StackOverflow. Mentre la tua risposta è apprezzata, è meglio dare più di un semplice codice. Potresti aggiungere un ragionamento alla tua risposta o una piccola spiegazione? Vedi questa pagina per altre idee su come espandere la tua risposta.
Celeo,

0

Questo è quello che uso ogni giorno:

#
# Check if a variable is set
#   param1  name of the variable
#
function is_set()
{
    [[ -n "${1}" ]] && test -n "$(eval "echo "\${${1}+x}"")"
}

Funziona bene su Linux e Solaris fino alla bash 3.0.

bash-3.00$ myvar="TEST"
bash-3.00$ is_set myvar ; echo $?
0
bash-3.00$ mavar=""
bash-3.00$ is_set myvar ; echo $?
0
bash-3.00$ unset myvar
bash-3.00$ is_set myvar ; echo $?
1

1
Poiché Bash 2.0+ è disponibile l'espansione indiretta. È possibile sostituire il lungo e complesso (con un eval incluso anche) test -n "$(eval "echo "\${${1}+x}"")"per l'equivalente:[[ ${!1+x} ]]

0

Mi piacciono le funzioni ausiliarie per nascondere i dettagli grezzi di bash. In questo caso, aggiungendo ancora più rozzazza (nascosta):

# The first ! negates the result (can't use -n to achieve this)
# the second ! expands the content of varname (can't do ${$varname})
function IsDeclared_Tricky
{
  local varname="$1"
  ! [ -z ${!varname+x} ]
}

Dato che per la prima volta ho avuto dei bug in questa implementazione (ispirata alle risposte di Jens e Lionel), ho trovato una soluzione diversa:

# Ask for the properties of the variable - fails if not declared
function IsDeclared()
{
  declare -p $1 &>/dev/null
}

Trovo che sia più diretto, più timido e più facile da capire / ricordare. Il caso di test mostra che è equivalente:

function main()
{
  declare -i xyz
  local foo
  local bar=
  local baz=''

  IsDeclared_Tricky xyz; echo "IsDeclared_Tricky xyz: $?"
  IsDeclared_Tricky foo; echo "IsDeclared_Tricky foo: $?"
  IsDeclared_Tricky bar; echo "IsDeclared_Tricky bar: $?"
  IsDeclared_Tricky baz; echo "IsDeclared_Tricky baz: $?"

  IsDeclared xyz; echo "IsDeclared xyz: $?"
  IsDeclared foo; echo "IsDeclared foo: $?"
  IsDeclared bar; echo "IsDeclared bar: $?"
  IsDeclared baz; echo "IsDeclared baz: $?"
}

main

Il caso di test mostra anche che NON dichiara var (a meno che local varnon sia seguito da '='). Per un po 'di tempo ho pensato di dichiarare le variabili in questo modo, solo per scoprire ora che ho semplicemente espresso la mia intenzione ... È una no-op, immagino.

IsDeclared_Tricky xyz: 1
IsDeclared_Tricky foo: 1
IsDeclared_Tricky bar: 0
IsDeclared_Tricky baz: 0
IsDeclared xyz: 1
IsDeclared foo: 1
IsDeclared bar: 0
IsDeclared baz: 0

BONUS: caso d'uso

Uso principalmente questo test per fornire (e restituire) i parametri alle funzioni in modo un po ' "elegante" e sicuro (quasi simile a un'interfaccia ...):

#auxiliary functions
function die()
{
  echo "Error: $1"; exit 1
}

function assertVariableDeclared()
{
  IsDeclared "$1" || die "variable not declared: $1"
}

function expectVariables()
{
  while (( $# > 0 )); do
    assertVariableDeclared $1; shift
  done
}

# actual example
function exampleFunction()
{
  expectVariables inputStr outputStr
  outputStr="$inputStr world!"
}

function bonus()
{
  local inputStr='Hello'
  local outputStr= # remove this to trigger error
  exampleFunction
  echo $outputStr
}

bonus

Se chiamato con tutti richiede variabili dichiarate:

Ciao mondo!

altro:

Errore: variabile non dichiarata: outputStr


0

Dopo aver scremato tutte le risposte, funziona anche:

if [[ -z $SOME_VAR ]]; then read -p "Enter a value for SOME_VAR: " SOME_VAR; fi
echo "SOME_VAR=$SOME_VAR"

se non metti SOME_VARinvece quello che ho $SOME_VAR, lo imposterà su un valore vuoto; $è necessario affinché questo funzioni.


non funzionerà quando avrai set -ueffetto che stamperà l' unboud variableerrore
Michael Heuberger,

0

Sommario

  • Utilizzare test -n "${var-}"per verificare se la variabile non è vuota (e quindi deve essere definita / impostata anche)
  • Utilizzare test ! -z "${var+}"per verificare se la variabile è definita / impostata (anche se è vuota)

Si noti che il primo caso d'uso è molto più comune negli script di shell e questo è ciò che di solito si desidera utilizzare.

Appunti

  • Questa soluzione dovrebbe funzionare in tutte le shell POSIX (sh, bash, zsh, ksh, dash)
  • Alcune delle altre risposte a questa domanda sono corrette ma possono essere fonte di confusione per le persone inesperte negli script di shell, quindi volevo fornire una risposta TLDR che sarebbe meno confusa per tali persone.

Spiegazione

Per capire come funziona questa soluzione, è necessario comprendere il comando POSIXtest e l'espansione dei parametri della shell POSIX ( spec ), quindi analizziamo le basi assolute necessarie per comprendere la risposta.

Il comando test valuta un'espressione e restituisce vero o falso (tramite il suo stato di uscita). L'operatore -nrestituisce true se l'operando è una stringa non vuota. Ad esempio, test -n "a"restituisce true, mentre test -n ""restituisce false. Ora, per verificare se una variabile non è vuota (il che significa che deve essere definita), è possibile utilizzare test -n "$var". Tuttavia, alcuni script di shell hanno un'opzione set ( set -u) che fa sì che qualsiasi riferimento a variabili indefinite emetta un errore, quindi se la variabile varnon è definita, l'espressione $acauserà un errore. Per gestire correttamente questo caso, è necessario utilizzare l'espansione della variabile, che indicherà alla shell di sostituire la variabile con una stringa alternativa se non è definita, evitando l'errore di cui sopra.

L'espansione della variabile ${var-}significa: se la variabile varnon è definita (anche chiamata "unset"), sostituirla con una stringa vuota. Quindi test -n "${var-}"restituirà true se $varnon è vuoto, che è quasi sempre ciò che si desidera controllare negli script di shell. Il controllo inverso, se $varnon è definito o non è vuoto, sarebbe test -z "${var-}".

Ora al secondo caso d'uso: verificare se la variabile varè definita, vuota o no. Questo è un caso d'uso meno comune e leggermente più complesso, e ti consiglierei di leggere l'ottima risposta di Lionels per comprenderlo meglio.


-1

Per verificare se un var è impostato o meno

var=""; [[ $var ]] && echo "set" || echo "not set"

3
Ciò fallirà se $varè impostato sulla stringa vuota.
Ciao Arrivederci,

-1

Se si desidera verificare che una variabile sia associata o non associata, funziona bene, anche dopo aver attivato l'opzione nounset:

set -o noun set

if printenv variableName >/dev/null; then
    # variable is bound to a value
else
    # variable is unbound
fi

Penso che tu intenda set -o nounset, no set -o noun set. Questo funziona solo per le variabili che sono state modificate export. Cambia anche le tue impostazioni in un modo scomodo da annullare.
Keith Thompson,
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.