Nota
Sto dando una risposta fortemente incentrata su Bash a causa del bash
tag.
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 var
genera un declare
comando che imposta la variabile var
su qualunque sia il suo tipo e valore attuali e restituisce il codice di stato 0
(successo). Se var
non dichiarato, declare -p var
genera un messaggio di errore stderr
e restituisce il codice di stato 1
. Utilizzando &>/dev/null
, reindirizza sia il normale stdout
che l' stderr
output 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 -p
invece 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
, eval
s 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 -p
perché è lo stesso declare -p
delle 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.
if test $# -gt 0; then printf 'arg <%s>\n' "$@"; fi
.