Примітка
Я даю сильно зосереджену відповідь Баша через bashтег.
Коротка відповідь
Поки ви маєте справу лише з названими змінними в Bash, ця функція завжди повинна говорити вам, чи була змінена установлена, навіть якщо це порожній масив.
variable-is-set() {
declare -p "$1" &>/dev/null
}
Чому це працює
У Bash (щонайменше, як 3.0), якщо varце оголошена / встановлена змінна, declare -p varвиводиться declareкоманда, яка встановила б змінну varв залежності від її поточного типу та значення та повертає код стану 0(успіх). Якщо varце не визначено, declare -p varвиводить повідомлення про помилку stderrта повертає код статусу 1. Використовуючи &>/dev/null, переадресовує як звичайний, так stdoutі stderrвихідний на/dev/null ніколи не було видно, і без зміни коду статусу. Таким чином, функція повертає лише код статусу.
Чому інші методи (іноді) провалюються в Bash
[ -n "$var" ]: Це перевіряє, чи ${var[0]}немає порожнього. (У Bash $var- те саме, що і ${var[0]}.)
[ -n "${var+x}" ]: Це перевіряє лише, якщо ${var[0]}встановлено.
[ "${#var[@]}" != 0 ]: Це перевіряє, чи встановлено принаймні один індекс $var.
Коли цей метод не працює в Bash
Це працює тільки для іменованих змінних ( в тому числі $_), а НЕ деяких спеціальних змінних ( $!, $@, $#, $$, $*, $?, $-, $0, $1, $2, ..., і будь-який я забув). Оскільки жоден з них не є масивами, стиль POSIX [ -n "${var+x}" ]працює для всіх цих спеціальних змінних. Але будьте обережні, щоб перетворити його у функцію, оскільки багато спеціальних змінних змінюють значення / існування, коли викликаються функції.
Примітка про сумісність оболонки
Якщо ваш сценарій має масиви, і ви намагаєтеся зробити його сумісним із якомога більшою кількістю оболонок, тоді typeset -pзамість цього використовуйте їх declare -p. Я читав, що ksh підтримує лише колишній, але не зміг перевірити це. Я знаю, що Bash 3.0+ і Zsh 5.5.1 кожен підтримують і те, typeset -pі declare -pрізниться лише тим, що одне є альтернативою іншому. Але я не перевіряв відмінності за межами цих двох ключових слів, і я не перевіряв інші оболонки.
Якщо вам потрібен, щоб ваш сценарій сумісний з POSIX sh, ви не можете використовувати масиви. Без масивів [ -n "{$var+x}" ]працює.
Код порівняння для різних методів у Bash
Ця функція знімає змінну var, eval- пройдений код, проводить тести, щоб визначити, чи varвстановлений evalкод d, і, нарешті, показує отримані коди статусу для різних тестів.
Я пропускаю test -v var, [ -v var ]і [[ -v var ]]тому, що вони дають однакові результати, ніж стандарт POSIX [ -n "${var+x}" ], вимагаючи Bash 4.2+. Я також пропускаю, typeset -pтому що це те саме, що і declare -pв оболонках, які я протестував (Bash 3.0 через 5.0 і 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
}
Код тестового випадку
Зауважте, що результати тестування можуть бути несподіваними через те, що Bash трактує нечислові індекси масиву як "0", якщо змінна не була оголошена асоціативним масивом. Також асоціативні масиви дійсні лише в 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
Тестовий вихід
Випробовувані Мнемоніки в заголовку рядки відповідають [ -n "$var" ], [ -n "${var+x}" ], [ "${#var[@]}" != 0 ], і declare -p var, відповідно.
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
Підсумок
declare -p var &>/dev/null є (100%?) надійним для тестування названих змінних в Bash, щонайменше, від 3.0.
[ -n "${var+x}" ] надійний у ситуаціях, сумісних з POSIX, але не може обробляти масиви.
- Інші тести існують для перевірки, чи змінна непорожня, і для перевірки оголошених змінних в інших оболонках. Але ці тести не підходять ні для сценаріїв Bash, ні для POSIX.
if test $# -gt 0; then printf 'arg <%s>\n' "$@"; fi.