Come posso verificare se esiste una variabile in un'istruzione 'if'?


69

Devo verificare l'esistenza di una variabile in una ifdichiarazione. Qualcosa per l'effetto di:

if [ -v $somevar ]
then
    echo "Variable somevar exists!"
else
    echo "Variable somevar does not exist!"

E la domanda più vicina a questa era questa , che in realtà non risponde alla mia domanda.


Se si desidera impostare $somevarun valore / stringa se variabile non esiste: ${somevar:=42}.
Cyrus,

Personalmente, tendo a controllare solo il vuoto ( [ -n "$var" ]o [ ! -z "$var" ]). Penso che i controlli di esistenza / inesistenza siano troppo sottili e preferisco il mio codice approssimativo e semplice.
PSkocik,

Perché hai bisogno di quello vs [ -n "$var" ]? Correlati: stackoverflow.com/questions/3601515/...
Ciro Santilli新疆改造中心法轮功六四事件

Risposte:


97

In bash moderno (versione 4.2 e successive):

[[ -v name_of_var ]]

Da help test:

-v VAR, True se è impostata la variabile shell VAR


3
Funziona anche con staffe singoli: [ -v name_of_var ].
Meuh

7
attenzione che per hash e array, restituisce false a meno che la variabile non abbia un elemento di chiave / indice "0". Per namerefs, verifica se il target è definito. Non funziona per i parametri speciali come $1, $-, $#...
Stéphane Chazelas

4
Questa funzione è solo nell'integrato bash testo [; Non è disponibile in /usr/bin/test. Confronta man testcon help test.
Mark Lakata,

@MarkLakata Esatto, perché i comandi esterni non sono in grado di conoscere lo stato interno della shell.
Chris Down,

umm non dovrebbe essere sempre [[-v "$ name_of_var"]]?
Alexander Mills,

24

Dipende da cosa vuoi dire esiste .

Ha una variabile che è stato dichiarato, ma non assegnato esiste ?

Does una variabile di matrice (o hash) che è stata assegnata una lista vuota esistono ?

Ha una variabile nameref che punta a una variabile che attualmente non è assegnato esiste ?

Lei ritiene $-, $#, $1variabili? (POSIX no).

Nelle conchiglie tipo Bourne, il modo canonico è:

if [ -n "${var+set}" ]; then
  echo '$var was set'
fi

Che lavora per variabili scalari e altri parametri per sapere se una variabile è stato assegnato un valore (vuoto o meno, automaticamente, dall'ambiente, assigments, read, foro altro).

Per le shell che hanno un comando typeseto declare, che non riporta come impostato le variabili che sono state dichiarate ma non assegnate se non in zsh.

Per le shell che supportano le matrici, ad eccezione di yashe zshche non riportano come variabili array impostate a meno che non sia stato impostato l'elemento di indice 0.

Per bash(ma non ksh93ne zsh), per le variabili di tipo array associativo , che sarebbe senza segnalarli come insieme a meno che il loro elemento di tasto "0" è stato impostato.

Per ksh93e bash, per le variabili di tipo nameref , ciò restituisce true solo se la variabile a cui fa riferimento nameref viene considerata impostata .

Per ksh, zshe bash, un approccio potenzialmente migliore potrebbe essere:

if ((${#var[@]})); then
  echo '$var (or the variable it references for namerefs) or any of its elements for array/hashes has been set'
fi

Per ksh93, zshe bash4.4 o superiore, c'è anche:

if typeset -p var 2> /dev/null | grep -q '^'; then
  echo '$var exists'
fi

Che riporterà le variabili che sono state impostate o dichiarate.


1
declare -p/ typeset -pfunziona bashanche adesso.
Cas

1
@cas, no. Non per variabili dichiarate ma non impostate. Prova bash -c 'typeset -i a; typeset -p a'e confronta con ksh93o zsh.
Stéphane Chazelas,

+1 Grazie per indicarmi qui. Vedi anche la mia domanda unix.stackexchange.com/q/280893/674
Tim

@cas, che è cambiato con bash-4.4 (rilasciato a settembre 2016), l'ho modificato in.
Stéphane Chazelas,

9

Come menzionato nella risposta su SO , ecco un modo per verificare:

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

dove $ {somevar + x} è un'espansione del parametro che restituisce valore null se var non è impostato e sostituisce la stringa "x" in caso contrario.

L'uso -n, come suggerito dall'altra risposta, verificherà solo se la variabile contiene una stringa vuota. Non verificherà la sua esistenza.


2
Devi citare $somevarper gestire IFS=x. O quello o la citazione x.
Mikeserv,

1
@mikeserv Grazie, mi piace conoscere i casi limite :) Intendi if [ -z "${somevar+x}" ]? La quotazione sarebbe ancora richiesta all'interno [[e ]]?
Tom Hale,

@TomHale - sì, in rari casi. le [ testroutine accettano i parametri della riga di comando e quindi le consuete espansioni e interpretazioni come ordinate nel solito modo dovrebbero essere invocate per rendere all'atto del richiamo del test applicato ciò che si dovrebbe far leggere in tal modo da qualsiasi riga di comando del programma. test {! + "!"}
mikeserv

@mikeserv Immagino che il tuo sì sia alla mia seconda domanda ... Anche il primo è un sì?
Tom Hale,

1
+1; questo sembra essere il modo più semplice per farlo quando set -uè in vigore e la versione di Bash è pre-4.2.
Kyle Strand

4

POSIXly:

! (: "${somevar?}") 2>/dev/null && echo somevar unset

oppure puoi lasciare che la tua shell mostri il messaggio per te:

(: "${somevar?}")
zsh: somevar: parameter not set

@mikeserv: Sì, certo, ci sono molti modi per farlo. In primo luogo, penso che lo duplicherò con questo . Ma in questa domanda, l'OP vuole solo controllare, non ha affermato di voler uscire o riferire se la variabile non è stata impostata, quindi sono arrivato con un check in subshell.
cuonglm,

2
Beh, lo so, ma è proprio perché devi farlo in una subshell come quella che indica che potrebbe non essere il modo migliore per testare qui - questa è un'azione di arresto in caso di fallimento, non consente alcun mezzo semplice da gestire un fallimento. Portabilmente anche un trappuò funzionare solo su EXIT. Questo è tutto ciò che sto dicendo: semplicemente non si applica molto bene come pass / fail. E non sono nemmeno io a parlare - l'ho già fatto prima e mi è bastato un piccolo commento in questo modo per convincermi. Quindi, ho pensato di pagarlo in avanti.
Mikeserv,

1
@mikeserv: Bene, bene, è un buon punto. Mi chiedo solo, aggiungendo che può confondere l'OP con la sintassi :)
cuonglm

2
if set|grep '^somevar=' >/dev/null;then
    echo "somevar exists"
else
    echo "does not exist"
fi

Immagino che questo sia in qualche modo inefficiente, ma è molto semplice e shcompatibile, ed è proprio quello di cui ho bisogno.
hoijui,

2
printf ${var+'$var exists!\n'}

... non stampa nulla quando non lo fa. O...

printf $"var does%${var+.}s exist%c\n" \ not !

... te lo dirò in entrambi i modi.

è possibile utilizzare il valore restituito di un test per espandere dinamicamente la stringa di formato appropriata per la propria condizione:

[ "${var+1}" ]
printf $"var does%.$?0s exist%c\n" \ not !

Puoi anche printffallire in base a una sostituzione ...

printf $"var does%${var+.}s exist%c\n%.${var+b}d" \
        \ not ! \\c >&"$((2${var+-1}))" 2>/dev/null

... che stampa $var does not exist!su stderr e restituisce diverso da 0 quando $varnon è impostato, ma stampa $var does exist!su stdout e restituisce 0 quando $varè impostato.


1

Questa semplice linea funziona (e funziona sulla maggior parte delle shell POSIX):

${var+"false"} && echo "var is unset"

Oppure, scritto in una forma più lunga:

unset var

if ${var+"false"}
then
   echo "var is unset"
fi

L'espansione è:

  • Se var ha un valore (anche null), false viene sostituito
  • Se var ha "nessun valore", viene sostituito "nessun valore" (null).

L' ${var+"false"}espansione si espande in "null" di "false".
Quindi, "niente" o "falso" viene eseguito e il codice di uscita impostato.

Non è necessario chiamare il comando test( [o [[) poiché il valore di uscita è impostato dall'esecuzione dell'espansione stessa.


Sì, tranne in alcune vecchie versioni di zsh in emulazione sh quando $IFScontiene f, a, l, s o e. Come per altre risposte, c'è il caso di matrici, hash o altri tipi di variabili che si potrebbero voler menzionare.
Stéphane Chazelas,

@ StéphaneChazelas che ho scritto most POSIX shells. most significaIn the greatest number of instances , non tutto. ... ... Quindi sì, in condizioni oscure when $IFS contains f, a, l, s or ee per qualche oscuro involucro some old versions of zshquesto fallisce: che shock !. Dovrei presumere che tale bug sia stato risolto molto tempo fa. ... ... Stai proponendo di scrivere codice per shell rotte da tempo ?.

@ StéphaneChazelas Inoltre: il titolo della domanda è molto specifico: Bash.

zebra binaria ???
Mikeserv,

0

Il modo di guscio puro:

[ "${var+1}" ] || echo "The variable has not been set"

Script di prova:

#!/bin/sh
echo "Test 1, var has not yet been created"
[ "${var+1}" ] || echo "The variable has not been set"

echo "Test 2, var=1"
var=1
[ "${var+1}" ] || echo "The variable has not been set"

echo "Test 3, var="
var=
[ "${var+1}" ] || echo "The variable has not been set"

echo "Test 4, unset var"
unset var
[ "${var+1}" ] || echo "The variable has not been set"
echo "Done"

risultati:

Test 1, var has not yet been created
The variable has not been set
Test 2, var=1
Test 3, var=
Test 4, unset var
The variable has not been set
Done

1
Non riesce quando la variabile è impostata su stringa nulla.
Tom Hale,

0

Con bash 4.4.19 per me ha funzionato. Ecco un esempio completo

$export MAGENTO_DB_HOST="anyvalue"

#!/bin/bash

if [ -z "$MAGENTO_DB_HOST" ]; then
    echo "Magento variable not set"
else
    echo $MAGENTO_DB_HOST
fi

-1

Non è possibile utilizzare il ifcomando per verificare l'esistenza delle variabili dichiarate in bash, tuttavia l' -vopzione esiste nella bash più recente, ma non è portatile e non è possibile utilizzarla nelle bashversioni precedenti . Perché quando si utilizza una variabile, se non esiste, nascerà allo stesso tempo.

Ad esempio, immagina di non aver usato o assegnato un valore alla MYTESTvariabile, ma quando usi il comando echo non ti mostra nulla! O se lo stai usando if [ -z $MYTEST ]ha restituito un valore zero! Non ha restituito un altro stato di uscita, che indica che questa variabile non esiste!

Ora hai due soluzioni (senza -vopzione):

  1. Usando il declarecomando.
  2. Usando il setcomando.

Per esempio:

MYTEST=2
set | grep MYTEST
declare | grep MYTEST

Ma sfortunatamente questi comandi mostrano anche le funzioni caricate in memoria! È possibile utilizzare il declare -p | grep -q MYTEST ; echo $?comando per un risultato più pulito.


-1

Funzione per verificare se la variabile è dichiarata / non impostata

compreso vuoto $array=()


Oltre alla risposta di @ Gilles

case " ${!foobar*} " in
  *" foobar "*) echo "foobar is declared";;
  *) echo "foobar is not declared";;
esac

- che non ho trovato un modo per incapsulare all'interno di una funzione - Vorrei aggiungere una versione semplice, che si basa in parte su Richard Hansen 's risposta , ma lo fa anche l'indirizzo della trappola che si verifica con un vuoto array=():

# The first parameter needs to be the name of the variable to be checked.
# (See example below)

var_is_declared() {
    { [[ -n ${!1+anything} ]] || declare -p $1 &>/dev/null;}
}

var_is_unset() {
    { [[ -z ${!1+anything} ]] && ! declare -p $1 &>/dev/null;} 
}
  • Testando prima se la variabile è (un) impostata, la chiamata a dichiarare può essere evitata, se non necessaria.
  • Se tuttavia $1contiene il nome di un vuoto $array=(), la chiamata a dichiarare assicurerebbe che otteniamo il risultato giusto
  • Non ci sono mai molti dati passati a / dev / null poiché declare viene chiamato solo se la variabile non è impostata o un array vuoto.


Con il seguente codice è possibile testare le funzioni:

( # start a subshell to encapsulate functions/vars for easy copy-paste into the terminal
  # do not use this extra parenthesis () in a script!

var_is_declared() {
    { [[ -n ${!1+anything} ]] || declare -p $1 &>/dev/null;}
}

var_is_unset() {
    { [[ -z ${!1+anything} ]] && ! declare -p $1 &>/dev/null;} 
}

:;       echo -n 'a;       '; var_is_declared a && echo "# is declared" || echo "# is not declared"
a=;      echo -n 'a=;      '; var_is_declared a && echo "# is declared" || echo "# is not declared"
a="sd";  echo -n 'a="sd";  '; var_is_declared a && echo "# is declared" || echo "# is not declared"
a=();    echo -n 'a=();    '; var_is_declared a && echo "# is declared" || echo "# is not declared"
a=("");  echo -n 'a=("");  '; var_is_declared a && echo "# is declared" || echo "# is not declared"
unset a; echo -n 'unset a; '; var_is_declared a && echo "# is declared" || echo "# is not declared"
echo ;
:;       echo -n 'a;       '; var_is_unset a && echo "# is unset" || echo "# is not unset"
a=;      echo -n 'a=;      '; var_is_unset a && echo "# is unset" || echo "# is not unset"
a="foo"; echo -n 'a="foo"; '; var_is_unset a && echo "# is unset" || echo "# is not unset"
a=();    echo -n 'a=();    '; var_is_unset a && echo "# is unset" || echo "# is not unset"
a=("");  echo -n 'a=("");  '; var_is_unset a && echo "# is unset" || echo "# is not unset"
unset a; echo -n 'unset a; '; var_is_unset a && echo "# is unset" || echo "# is not unset"
)

Lo script dovrebbe tornare

a;       # is not declared
a=;      # is declared
a="foo"; # is declared
a=();    # is declared
a=("");  # is declared
unset a; # is not declared

a;       # is unset
a=;      # is not unset
a="foo"; # is not unset
a=();    # is not unset
a=("");  # is not unset
unset a; # is unset

-1

funzione bash che funziona sia per i tipi scalari che per quelli di array :

definizione

has_declare() { # check if variable is set at all
    local "$@" # inject 'name' argument in local scope
    &>/dev/null declare -p "$name" # return 0 when var is present
}

invocazione

if has_declare name="vars_name" ; then
   echo "variable present: vars_name=$vars_name"
fi
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.