Come capire se una stringa non è definita in uno script di shell bash?
Domanda
Se voglio verificare la stringa nulla lo farei
[ -z $mystr ]
ma cosa succede se voglio verificare se la variabile è stata definita affatto? O non c'è distinzione negli script bash?
Soluzione
Penso che la risposta che stai cercando sia implicita (se non dichiarata) da Vinko 's rispondi , sebbene non sia spiegato semplicemente. Per distinguere se VAR è impostato ma vuoto o no, puoi usare:
if [ -z "${VAR+xxx}" ]; then echo VAR is not set at all; fi
if [ -z "$VAR" ] && [ "${VAR+xxx}" = "xxx" ]; then echo VAR is set but empty; fi
Probabilmente puoi combinare i due test sulla seconda riga in uno con:
if [ -z "$VAR" -a "${VAR+xxx}" = "xxx" ]; then echo VAR is set but empty; fi
Tuttavia, se leggi la documentazione per Autoconf, scoprirai che non raccomandano di combinare i termini con '-a
' e raccomandano di usare test semplici separati combinati con &&
. Non ho riscontrato un sistema in cui si è verificato un problema; ciò non significa che non esistessero (ma probabilmente sono estremamente rari in questi giorni, anche se non erano così rari in un lontano passato).
Puoi trovare i dettagli di questi e altri relativi espansioni dei parametri della shell , test
o < => comando e espressioni condizionali nel manuale di Bash.
Di recente mi è stato chiesto via e-mail di questa risposta con la domanda:
Usi due test e capisco bene il secondo, ma non il primo. Più precisamente non capisco la necessità di un'espansione variabile
if [ -z "${VAR+xxx}" ]; then echo VAR is not set at all; fi
Questo non compirebbe lo stesso?
if [ -z "${VAR}" ]; then echo VAR is not set at all; fi
Domanda corretta: la risposta è "No, la tua alternativa più semplice non fa la stessa cosa".
Supponi di scriverlo prima del tuo test:
VAR=
Il tuo test dirà " VAR non è impostato affatto " ;, ma il mio dirà (implicitamente perché non riecheggia nulla) " VAR è impostato ma il suo valore potrebbe essere vuoto & ;. quot Prova questo script:
(
unset VAR
if [ -z "${VAR+xxx}" ]; then echo JL:1 VAR is not set at all; fi
if [ -z "${VAR}" ]; then echo MP:1 VAR is not set at all; fi
VAR=
if [ -z "${VAR+xxx}" ]; then echo JL:2 VAR is not set at all; fi
if [ -z "${VAR}" ]; then echo MP:2 VAR is not set at all; fi
)
L'output è:
JL:1 VAR is not set at all
MP:1 VAR is not set at all
MP:2 VAR is not set at all
Nella seconda coppia di test, la variabile è impostata, ma è impostata sul valore vuoto. Questa è la distinzione che fanno le notazioni [
e ${VAR=value}
. Idem per ${VAR:=value}
e ${VAR-value}
e ${VAR:-value}
e ${VAR+value}
e così via.
Come Gili sottolinea nel suo risposta , se si esegue ${VAR:+value}
con l'opzione bash
, la risposta di base sopra non riesce con set -o nounset
. È facilmente risolto:
if [ -z "${VAR+xxx}" ]; then echo VAR is not set at all; fi
if [ -z "${VAR-}" ] && [ "${VAR+xxx}" = "xxx" ]; then echo VAR is set but empty; fi
Oppure potresti annullare l'opzione unbound variable
con set +u
(set -u
è equivalente a <=>).
Altri suggerimenti
~> if [ -z $FOO ]; then echo "EMPTY"; fi
EMPTY
~> FOO=""
~> if [ -z $FOO ]; then echo "EMPTY"; fi
EMPTY
~> FOO="a"
~> if [ -z $FOO ]; then echo "EMPTY"; fi
~>
-z funziona anche per variabili non definite. Per distinguere tra un indefinito e uno definito useresti le cose elencate qui o, con spiegazioni più chiare , qui .
Il modo più pulito sta usando l'espansione come in questi esempi. Per ottenere tutte le opzioni, consultare la sezione Espansione dei parametri del manuale.
Parola alternativa:
~$ unset FOO
~$ if test ${FOO+defined}; then echo "DEFINED"; fi
~$ FOO=""
~$ if test ${FOO+defined}; then echo "DEFINED"; fi
DEFINED
Valore predefinito:
~$ FOO=""
~$ if test "${FOO-default value}" ; then echo "UNDEFINED"; fi
~$ unset FOO
~$ if test "${FOO-default value}" ; then echo "UNDEFINED"; fi
UNDEFINED
Ovviamente useresti uno di questi in modo diverso, mettendo il valore desiderato invece di "valore predefinito" e usando direttamente l'espansione, se appropriato.
Guida di scripting bash avanzata, 10.2. Sostituzione parametri:
- $ {var + blahblah}: se var è definito, "blahblah" è sostituito dal espressione, altrimenti null viene sostituito
- $ {var-blahblah}: se var è definito, viene esso stesso sostituito, altrimenti "blahblah" è sostituito
- $ {var? blahblah}: se var è definito, viene sostituito, altrimenti il esiste una funzione con "blahblah" come messaggio di errore.
per basare la logica del programma sul fatto che la variabile $ mystr sia definita o meno, è possibile effettuare le seguenti operazioni:
isdefined=0
${mystr+ export isdefined=1}
ora, se isdefined = 0 la variabile non era definita, se isdefined = 1 la variabile era definita
Questo modo di controllare le variabili è migliore della risposta di cui sopra perché è più elegante, leggibile e se la shell bash è stata configurata per errori sull'uso di variabili non definite (set -u)
, lo script terminerà prematuramente.
Altre cose utili:
avere un valore predefinito di 7 assegnato a $ mystr se non definito, e lasciarlo intatto altrimenti:
mystr=${mystr- 7}
per stampare un messaggio di errore ed uscire dalla funzione se la variabile non è definita:
: ${mystr? not defined}
Fai attenzione qui che ho usato ':' per non avere il contenuto di $ mystr eseguito come comando nel caso in cui sia definito.
Un riepilogo dei test.
[ -n "$var" ] && echo "var is set and not empty"
[ -z "$var" ] && echo "var is unset or empty"
[ "${var+x}" = "x" ] && echo "var is set" # may or may not be empty
[ -n "${var+x}" ] && echo "var is set" # may or may not be empty
[ -z "${var+x}" ] && echo "var is unset"
[ -z "${var-x}" ] && echo "var is set and empty"
https://stackoverflow.com/a/9824943/14731 contiene una risposta migliore (una più leggibile e funziona con set -o nounset
abilitato). Funziona più o meno in questo modo:
if [ -n "${VAR-}" ]; then
echo "VAR is set and is not empty"
elif [ "${VAR+DEFINED_BUT_EMPTY}" = "DEFINED_BUT_EMPTY" ]; then
echo "VAR is set, but empty"
else
echo "VAR is not set"
fi
Il modo esplicito di verificare la presenza di una variabile definita sarebbe:
[ -v mystr ]
un'altra opzione: l'elenco " indici di array " espansione :
$ unset foo
$ foo=
$ echo ${!foo[*]}
0
$ foo=bar
$ echo ${!foo[*]}
0
$ foo=(bar baz)
$ echo ${!foo[*]}
0 1
l'unica volta che questo si espande nella stringa vuota è quando foo
non è impostato, quindi puoi controllarlo con la stringa condizionale:
$ unset foo
$ [[ ${!foo[*]} ]]; echo $?
1
$ foo=
$ [[ ${!foo[*]} ]]; echo $?
0
$ foo=bar
$ [[ ${!foo[*]} ]]; echo $?
0
$ foo=(bar baz)
$ [[ ${!foo[*]} ]]; echo $?
0
dovrebbe essere disponibile in qualsiasi versione bash > = 3.0
non perdere ancora questa bici, ma volevo aggiungere
shopt -s -o nounset
è qualcosa che potresti aggiungere all'inizio di uno script, il che causerà un errore se le variabili non sono dichiarate in nessun punto dello script. Il messaggio che visualizzerai è unbound variable
, ma come altri citano non catturerà una stringa vuota o un valore null. Per accertarci che ogni singolo valore non sia vuoto, possiamo testare una variabile poiché viene espansa con ${mystr:?}
, nota anche come espansione del simbolo del dollaro, che verrebbe a mancare con parameter null or not set
.
Il Bash Reference Manual è una fonte autorevole di informazioni su bash.
Ecco un esempio di test di una variabile per vedere se esiste:
if [ -z "$PS1" ]; then
echo This shell is not interactive
else
echo This shell is interactive
fi
(Da sezione 6.3.2 .)
Nota che lo spazio bianco dopo l'apertura [
e prima di ]
è non opzionale .
Suggerimenti per gli utenti di Vim
Avevo uno script che aveva diverse dichiarazioni come segue:
export VARIABLE_NAME="$SOME_OTHER_VARIABLE/path-part"
Ma volevo che rimandassero a qualsiasi valore esistente. Quindi li ho riscritti per assomigliare a questo:
if [ -z "$VARIABLE_NAME" ]; then
export VARIABLE_NAME="$SOME_OTHER_VARIABLE/path-part"
fi
Sono stato in grado di automatizzare questo in vim usando una regex veloce:
s/\vexport ([A-Z_]+)\=("[^"]+")\n/if [ -z "$\1" ]; then\r export \1=\2\rfi\r/gc
Questo può essere applicato selezionando visivamente le righe pertinenti, quindi digitando :
. La barra dei comandi precompila con :'<,'>
. Incolla il comando sopra e premi invio.
Testato su questa versione di Vim:
VIM - Vi IMproved 7.3 (2010 Aug 15, compiled Aug 22 2015 15:38:58)
Compiled by root@apple.com
Gli utenti di Windows potrebbero desiderare terminazioni di linea diverse.
Ecco quello che penso sia un modo molto più chiaro per verificare se una variabile è definita:
var_defined() {
local var_name=$1
set | grep "^${var_name}=" 1>/dev/null
return $?
}
Usalo come segue:
if var_defined foo; then
echo "foo is defined"
else
echo "foo is not defined"
fi
set di chiamate senza argomenti .. genera tutti i parametri definiti ..
gli ultimi nell'elenco sarebbero quelli definiti nel tuo script.
così puoi indirizzare il suo output a qualcosa che possa capire quali cose sono definite e cosa no