Hinweis
Ich gebe aufgrund des bash
Tags eine stark auf Bash ausgerichtete Antwort .
Kurze Antwort
Solange Sie nur mit benannten Variablen in Bash arbeiten, sollte diese Funktion Ihnen immer mitteilen, ob die Variable festgelegt wurde, auch wenn es sich um ein leeres Array handelt.
variable-is-set() {
declare -p "$1" &>/dev/null
}
Warum das so ist
Wenn var
es sich bei Bash (mindestens bis 3.0) um eine deklarierte / gesetzte Variable handelt, wird declare -p var
ein declare
Befehl ausgegeben , der die Variable var
auf den aktuellen Typ und Wert setzt und den Statuscode 0
(Erfolg) zurückgibt . Wenn var
nicht deklariert ist, wird declare -p var
eine Fehlermeldung an den stderr
Statuscode ausgegeben und dieser zurückgegeben 1
. Leitet mit &>/dev/null
und leitet sowohl regulär stdout
als auch stderr
Ausgabe an um/dev/null
, wird nie gesehen und ohne den Statuscode zu ändern. Somit gibt die Funktion nur den Statuscode zurück.
Warum andere Methoden (manchmal) in Bash fehlschlagen
[ -n "$var" ]
: Dies prüft nur, ob ${var[0]}
es nicht leer ist. (In Bash $var
ist das gleiche wie ${var[0]}
.)
[ -n "${var+x}" ]
: Dies prüft nur, ob ${var[0]}
gesetzt ist.
[ "${#var[@]}" != 0 ]
: Dies prüft nur, ob mindestens ein Index von $var
gesetzt ist.
Wenn diese Methode in Bash fehlschlägt
Dies funktioniert nur bei benannten Variablen (einschließlich $_
), nicht bestimmten Sondergrößen ( $!
, $@
, $#
, $$
, $*
, $?
, $-
, $0
, $1
, $2
, ... und alle , die ich vergessen habe). Da es sich bei keinem dieser Arrays um Arrays handelt, [ -n "${var+x}" ]
funktioniert der POSIX-Stil für alle diese speziellen Variablen. Achten Sie jedoch darauf, dass Sie es nicht in eine Funktion einschließen, da viele spezielle Variablen beim Aufrufen von Funktionen Werte / Existenz ändern.
Hinweis zur Shell-Kompatibilität
Wenn Ihr Skript Arrays enthält und Sie versuchen, es mit so vielen Shells wie möglich kompatibel zu machen, sollten Sie die Verwendung von typeset -p
anstelle von in Betracht ziehen declare -p
. Ich habe gelesen, dass ksh nur das erstere unterstützt, konnte dies aber nicht testen. Ich weiß, dass Bash 3.0+ und Zsh 5.5.1 beide unterstützen typeset -p
und declare -p
sich nur dadurch unterscheiden, dass eines eine Alternative für das andere ist. Aber ich habe keine Unterschiede über diese beiden Schlüsselwörter hinaus getestet, und ich habe keine anderen Shells getestet.
Wenn Ihr Skript POSIX sh-kompatibel sein soll, können Sie keine Arrays verwenden. Ohne Arrays [ -n "{$var+x}" ]
funktioniert.
Vergleichscode für verschiedene Methoden in Bash
Diese Funktion deaktiviert die Variable var
, eval
s den übergebenen Code, führt Tests aus, um festzustellen, ob var
der eval
d-Code festgelegt ist, und zeigt schließlich die resultierenden Statuscodes für die verschiedenen Tests an.
Ich Skipping test -v var
, [ -v var ]
und [[ -v var ]]
weil sie identische Ergebnisse an den POSIX - Standard ergeben [ -n "${var+x}" ]
, während erfordern Bash 4.2+. Ich überspringe auch, typeset -p
weil es das gleiche ist wie declare -p
in den Shells, die ich getestet habe (Bash 3.0 bis 5.0 und 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
}
Testfallcode
Beachten Sie, dass Testergebnisse unerwartet sein können, da Bash nicht numerische Array-Indizes als "0" behandelt, wenn die Variable nicht als assoziatives Array deklariert wurde. Außerdem sind assoziative Arrays nur in Bash 4.0+ gültig.
# 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
Testausgabe
Die Test Mnemonik in der Kopfzeile entsprechen [ -n "$var" ]
, [ -n "${var+x}" ]
, [ "${#var[@]}" != 0 ]
, und declare -p var
ist.
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
Zusammenfassung
declare -p var &>/dev/null
ist (100%?) zuverlässig für das Testen benannter Variablen in Bash seit mindestens 3.0.
[ -n "${var+x}" ]
ist in POSIX-kompatiblen Situationen zuverlässig, kann jedoch keine Arrays verarbeiten.
- Es gibt andere Tests zum Überprüfen, ob eine Variable nicht leer ist, und zum Überprüfen auf deklarierte Variablen in anderen Shells. Diese Tests eignen sich jedoch weder für Bash- noch für POSIX-Skripte.
if test $# -gt 0; then printf 'arg <%s>\n' "$@"; fi
.