Come verificare se una variabile è stata definita in Bash prima della versione 4.2 con l'opzione shell set di nomi?


16

Per le versioni di Bash precedenti a "GNU bash, Versione 4.2" ci sono alternative equivalenti per l' -vopzione del testcomando? Per esempio:

shopt -os nounset
test -v foobar && echo foo || echo bar
# Output: bar
foobar=
test -v foobar && echo foo || echo bar
# Output: foo

-vnon è un'opzione per test, ma un operatore per le espressioni condizionali.
StackExchange per tutto

@Tim Sono tre cose, oltre ad essere un token, una stringa e parte di una linea: An optiona un comando test -v, an operatora a conditional expressione a unary test primaryfor [ ]. Non mescolare la lingua inglese con le definizioni di shell.

Risposte:


36

Portatile per tutte le shell POSIX:

if [ -n "${foobar+1}" ]; then
  echo "foobar is defined"
else
  echo "foobar is not defined"
fi

Fare che ${foobar:+1}se si vuole trattare foobarallo stesso modo se è vuota o non definita. È inoltre possibile utilizzare ${foobar-}per ottenere una stringa vuota quando foobarnon è definito e il valore di foobaraltrimenti (o inserire qualsiasi altro valore predefinito dopo il -).

In ksh, se foobarviene dichiarato ma non definito, come in typeset -a foobar, quindi si ${foobar+1}espande nella stringa vuota.

Zsh non ha variabili dichiarate ma non impostate: typeset -a foobarcrea un array vuoto.

In bash, le matrici si comportano in modo diverso e sorprendente. ${a+1}si espande solo 1se aè un array non vuoto, ad es

typeset -a a; echo ${a+1}    # prints nothing
e=(); echo ${e+1}            # prints nothing!
f=(''); echo ${f+1}          # prints 1

Lo stesso principio si applica agli array associativi: le variabili array vengono trattate come definite se hanno un insieme di indici non vuoto.

Un modo diverso e specifico per bash di verificare se è stata definita una variabile di qualsiasi tipo è verificare se è elencato in . Questo riporta array vuoti come definiti, diversamente , ma riporta variabili dichiarate ma non assegnate ( ) come indefinite.${!PREFIX*}${foobar+1}unset foobar; typeset -a foobar

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

Ciò equivale a testare il valore di ritorno di typeset -p foobarodeclare -p foobar , tranne che typeset -p foobarnon riesce su variabili dichiarate ma non assegnate.

In bash, come in ksh, set -o nounset; typeset -a foobar; echo $foobargenera un errore nel tentativo di espandere la variabile non definita foobar. A differenza di ksh, set -o nounset; foobar=(); echo $foobar(o echo "${foobar[@]}") attiva anche un errore.

Si noti che in tutte le situazioni qui descritte, si ${foobar+1}espande nella stringa vuota se e solo se $foobarcauserebbe un errore in set -o nounset.


1
E le matrici? Nella versione di Bash "GNU bash, versione 4.1.10 (4) -release (i686-pc-cygwin)" echo "${foobar:+1}"non stampa 1se è declare -a foobarstato precedentemente emesso e quindi foobarè un array indicizzato. declare -p foobarriporta correttamente declare -a foobar='()'. Funziona "${foobar:+1}"solo con variabili non array?
Tim Friske,

@TimFriske ${foobar+1}(senza il :, ho invertito due esempi nella mia risposta originale) è corretto per le matrici in bash se la tua definizione di "definito" è " $foobarfunzionerebbe sotto set -o nounset". Se la tua definizione è diversa, bash è un po 'strano. Vedi la mia risposta aggiornata.
Gilles 'SO- smetti di essere malvagio' il

1
Per quanto riguarda l'argomento "In bash, le matrici si comportano in modo diverso e sorprendente". il comportamento può essere spiegato dalle manpage bash (1), sezione "Array". Afferma che "Fare riferimento a una variabile di matrice senza un pedice equivale a fare riferimento alla matrice con un pedice di 0.". Pertanto, se non 0viene definito né un indice né una chiave per cui è vero a=(), ${a+1}non restituisce correttamente nulla.
Tim Friske,

1
@TimFriske So che l'implementazione di bash è conforme alla sua documentazione. Ma trattare un array vuoto come una variabile indefinita è un progetto davvero strano.
Gilles 'SO- smetti di essere malvagio' il

Preferisco il risultato da: Come verificare se una variabile è impostata in Bash? -> Il modo giusto ... Mi colpisce una corda di come ho cosa che questo dovrebbe al lavoro. Oserei dire, Windows ha un definedoperatore. Test potrebbe farlo; non può essere difficile ( um ....)
sarà l'

5

Per riassumere con la risposta di Gilles, ho inventato le seguenti regole:

  1. Utilizzare [[ -v foobar ]]per variabili in versione Bash> = 4.2.
  2. Utilizzare declare -p foobar &>/dev/nullper variabili array in versione Bash <4.2.
  3. Utilizzare (( ${foo[0]+1} ))o (( ${bar[foo]+1} ))per i pedici degli array indexed ( -a) e keyed -A( declare), rispettivamente. Le opzioni 1 e 2 non funzionano qui.

3

Uso la stessa tecnica per tutte le variabili in bash e funziona, ad esempio:

[ ${foobar} ] && echo "foobar is set" || echo "foobar is unset"

uscite:

foobar is unset

mentre

foobar=( "val" "val2" )
[ ${foobar} ] && echo "foobar is set" || echo "foobar is unset"

uscite:

foobar is set

Ho dovuto rimuovere [@] se l'array ha più di un valore.
Stein Inge Morisbak,

1
Funziona alla grande, purché tu voglia verificare se ha un valore, non se è stato definito. Cioè, foobar=""lo segnalerò foobar is unset. No aspetta, lo riprendo. Veramente verifica solo se il primo elemento è vuoto o meno, quindi sembra essere una buona idea solo se sai che la variabile NON è un array e ti preoccupi solo del vuoto, non della definizione.
Ron Burk,

funziona solo se i tuoi script sono in esecuzione con variabili indefinite consentite (no set -u)
Florian Heigl
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.