Uwaga
Daję odpowiedź bardzo skoncentrowaną na Bash z powodu bash
tagu.
Krótka odpowiedź
Tak długo, jak masz do czynienia z nazwanymi zmiennymi w Bash, ta funkcja powinna zawsze informować, czy zmienna została ustawiona, nawet jeśli jest to pusta tablica.
variable-is-set() {
declare -p "$1" &>/dev/null
}
Dlaczego to działa
W Bash (przynajmniej do 3.0), jeśli var
jest zmienną zadeklarowaną / ustawioną, wówczas declare -p var
wypisuje declare
polecenie, które ustawi zmienną var
na dowolny jej aktualny typ i wartość, i zwraca kod statusu 0
(sukces). Jeśli nie var
jest zadeklarowany, declare -p var
wyświetla komunikat o błędzie stderr
i zwraca kod stanu 1
. Użycie &>/dev/null
przekierowuje zarówno zwykłe, jak stdout
i stderr
wyjściowe /dev/null
, nigdy nie widoczne i bez zmiany kodu statusu. Dlatego funkcja zwraca tylko kod statusu.
Dlaczego inne metody (czasami) zawodzą w Bash
[ -n "$var" ]
: Sprawdza tylko, czy ${var[0]}
jest niepuste. (W Bash $var
jest taki sam jak ${var[0]}
.)
[ -n "${var+x}" ]
: To sprawdza tylko, czy ${var[0]}
jest ustawione.
[ "${#var[@]}" != 0 ]
: Sprawdza to tylko, czy $var
ustawiony jest przynajmniej jeden indeks .
Gdy ta metoda zawiedzie w Bash
To działa tylko dla nazwanych zmiennych (włącznie $_
) nie pewnych zmiennych specjalnych ( $!
, $@
, $#
, $$
, $*
, $?
, $-
, $0
, $1
, $2
, ..., a wszelkie mogę zapomnieć). Ponieważ żadna z nich nie jest tablicami, styl POSIX [ -n "${var+x}" ]
działa dla wszystkich tych zmiennych specjalnych. Ale uważaj na zawijanie go w funkcję, ponieważ wiele specjalnych zmiennych zmienia wartości / istnienie podczas wywoływania funkcji.
Uwaga dotycząca zgodności z powłoką
Jeśli twój skrypt ma tablice i próbujesz uczynić go kompatybilnym z jak największą liczbą powłok, rozważ użycie typeset -p
zamiast niego declare -p
. Czytałem, że ksh obsługuje tylko te pierwsze, ale nie byłem w stanie tego przetestować. Wiem, że Bash 3.0+ i Zsh 5.5.1 obsługują oba typeset -p
i declare -p
różnią się tylko tym, w którym jedno jest alternatywą dla drugiego. Ale nie testowałem różnic poza tymi dwoma słowami kluczowymi i nie testowałem innych powłok.
Jeśli potrzebujesz, aby skrypt był zgodny z POSIX sh, nie możesz używać tablic. Bez tablic [ -n "{$var+x}" ]
działa.
Kod porównawczy dla różnych metod w Bash
Ta funkcja resetuje zmienną var
, eval
s przekazany kod, uruchamia testy w celu ustalenia, czy var
jest ustawiona przez eval
kod d, a na koniec wyświetla wynikowe kody stanu dla różnych testów.
Ja omijając test -v var
, [ -v var ]
i [[ -v var ]]
ponieważ dają one takie same wyniki ze standardem POSIX [ -n "${var+x}" ]
, wymagając Basha wersji 4.2 lub nowszej. Pomijam również, typeset -p
ponieważ jest taki sam, jak declare -p
w testowanych przeze mnie powłokach (Bash 3.0 do 5.0 i 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
}
Kod przypadku testowego
Zauważ, że wyniki testu mogą być nieoczekiwane z powodu traktowania przez Bash indeksów tablic nienumerycznych jako „0”, jeśli zmienna nie została zadeklarowana jako tablica asocjacyjna. Również tablice asocjacyjne działają tylko w 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
Wyjście testowe
W mnemoniki badań odpowiadają w wierszu nagłówka, aby [ -n "$var" ]
, [ -n "${var+x}" ]
, [ "${#var[@]}" != 0 ]
i declare -p var
, odpowiednio.
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
Podsumowanie
declare -p var &>/dev/null
jest (100%?) niezawodny do testowania nazwanych zmiennych w Bash od co najmniej 3.0.
[ -n "${var+x}" ]
jest niezawodny w sytuacjach zgodnych z POSIX, ale nie obsługuje tablic.
- Istnieją inne testy do sprawdzania, czy zmienna nie jest pusta, oraz do sprawdzania deklarowanych zmiennych w innych powłokach. Ale testy te nie są odpowiednie dla skryptów Bash ani POSIX.
if test $# -gt 0; then printf 'arg <%s>\n' "$@"; fi
.