-vне вариант test, а оператор для условных выражений.
StackExchange для всех
@Tim Это три вещи, помимо токена, строки и части строки: optionот команды к test -v, от operatorк conditional expressionи unary test primaryдля [ ]. Не смешивайте английский язык с определениями оболочки.
Ответы:
36
Переносим на все оболочки POSIX:
if[-n "${foobar+1}"];then
echo "foobar is defined"else
echo "foobar is not defined"fi
Сделайте это, ${foobar:+1}если вы хотите обрабатывать foobarодинаково, независимо от того, пусто оно или не определено. Вы также можете использовать ${foobar-}для получения пустой строки, когда значение foobarundefined и значение foobarиначе (или поставить любое другое значение по умолчанию после -).
В ksh, если foobarобъявлен, но не определен, как в typeset -a foobar, то ${foobar+1}расширяется до пустой строки.
Zsh не имеет переменных, которые объявлены, но не установлены: typeset -a foobarсоздает пустой массив.
В bash массивы ведут себя по-другому и удивительно. ${a+1}расширяется только до 1if, если aэто не пустой массив, например
typeset -a a; echo ${a+1}# prints nothing
e=(); echo ${e+1}# prints nothing!
f=(''); echo ${f+1}# prints 1
Тот же принцип применяется к ассоциативным массивам: переменные массива обрабатываются как определенные, если они имеют непустой набор индексов.
Другой, специфичный для bash способ проверки того, была ли определена переменная любого типа, состоит в проверке того, находится ли она в списке . Это сообщает о пустых массивах, как определено, в отличие от , но сообщает объявленные, но неназначенные переменные ( ) как неопределенные.${!PREFIX*}${foobar+1}unset foobar; typeset -a foobar
case" ${!foobar*} "in*" foobar "*) echo "foobar is defined";;*) echo "foobar is not defined";;esac
В bash, как и в ksh, set -o nounset; typeset -a foobar; echo $foobarвыдает ошибку при попытке раскрыть неопределенную переменную foobar. В отличие от ksh, set -o nounset; foobar=(); echo $foobar(или echo "${foobar[@]}") также вызывает ошибку.
Обратите внимание, что во всех описанных здесь ситуациях, ${foobar+1}расширяется до пустой строки тогда и только тогда, $foobarкогда вызовет ошибку в set -o nounset.
Как насчет массивов? В версии Bash «GNU bash, версия 4.1.10 (4) -релиз (i686-pc-cygwin)» echo "${foobar:+1}"не печатает, 1если он declare -a foobarбыл выпущен ранее, и поэтому foobarявляется индексированным массивом. declare -p foobarправильно сообщает declare -a foobar='()'. Работает "${foobar:+1}"только для переменных, не являющихся массивами?
Тим Фриске
@TimFriske ${foobar+1}(без :, я перевернул два примера в своем первоначальном ответе) правильно для массивов в bash, если ваше определение «определено» - «будет $foobarработать под set -o nounset». Если ваше определение отличается, bash немного странный. Смотрите мой обновленный ответ.
Жиль "ТАК - перестань быть злым"
1
Что касается темы «В bash массивы ведут себя по-другому и удивительно». поведение может быть объяснено из man-страниц bash (1), раздел «Массивы». В нем говорится, что «Ссылка на переменную массива без нижнего индекса эквивалентна ссылке на массив с нижним индексом 0.». Таким образом, если ни 0индекс, ни ключ не определены так, как это верно a=(), то ${a+1}ничего не возвращает правильно.
Тим Фриске
1
@TimFriske Я знаю, что реализация bash соответствует его документации. Но обработка пустого массива как неопределенной переменной - действительно странный дизайн.
Подводя итог ответу Жиля, я сформулировал следующие правила:
Используйте [[ -v foobar ]]для переменных в версии Bash> = 4.2.
Используйте declare -p foobar &>/dev/nullдля переменных массива в версии Bash <4.2.
Используйте (( ${foo[0]+1} ))или (( ${bar[foo]+1} ))для индексов массивов indexed ( -a) и keyed ( -A) declareсоответственно. Варианты 1 и 2 здесь не работают.
Пришлось удалить [@], если массив имеет более одного значения.
Стейн Инге Морисбак
1
Это прекрасно работает, если вы хотите проверить, имеет ли оно значение, а не определено ли оно. Т.е. foobar=""потом сообщу об этом foobar is unset. Нет, подожди, я заберу это обратно. Действительно только проверяет, является ли первый элемент пустым или нет, так что будет хорошей идеей, если вы знаете, что переменная НЕ является массивом, и вам нужна только пустота, а не определенность.
Рон Берк
работает только в том случае, если ваши скрипты работают с неопределенными переменными (не задано -u)
-v
не вариантtest
, а оператор для условных выражений.option
от команды кtest -v
, отoperator
кconditional expression
иunary test primary
для[ ]
. Не смешивайте английский язык с определениями оболочки.Ответы:
Переносим на все оболочки POSIX:
Сделайте это,
${foobar:+1}
если вы хотите обрабатыватьfoobar
одинаково, независимо от того, пусто оно или не определено. Вы также можете использовать${foobar-}
для получения пустой строки, когда значениеfoobar
undefined и значениеfoobar
иначе (или поставить любое другое значение по умолчанию после-
).В ksh, если
foobar
объявлен, но не определен, как вtypeset -a foobar
, то${foobar+1}
расширяется до пустой строки.Zsh не имеет переменных, которые объявлены, но не установлены:
typeset -a foobar
создает пустой массив.В bash массивы ведут себя по-другому и удивительно.
${a+1}
расширяется только до1
if, еслиa
это не пустой массив, напримерТот же принцип применяется к ассоциативным массивам: переменные массива обрабатываются как определенные, если они имеют непустой набор индексов.
Другой, специфичный для bash способ проверки того, была ли определена переменная любого типа, состоит в проверке того, находится ли она в списке . Это сообщает о пустых массивах, как определено, в отличие от , но сообщает объявленные, но неназначенные переменные ( ) как неопределенные.
${!PREFIX*}
${foobar+1}
unset foobar; typeset -a foobar
Это эквивалентно проверке возвращаемого значения
typeset -p foobar
илиdeclare -p foobar
, за исключением того, что происходитtypeset -p foobar
сбой на объявленных, но неназначенных переменных.В bash, как и в ksh,
set -o nounset; typeset -a foobar; echo $foobar
выдает ошибку при попытке раскрыть неопределенную переменнуюfoobar
. В отличие от ksh,set -o nounset; foobar=(); echo $foobar
(илиecho "${foobar[@]}"
) также вызывает ошибку.Обратите внимание, что во всех описанных здесь ситуациях,
${foobar+1}
расширяется до пустой строки тогда и только тогда,$foobar
когда вызовет ошибку вset -o nounset
.источник
echo "${foobar:+1}"
не печатает,1
если онdeclare -a foobar
был выпущен ранее, и поэтомуfoobar
является индексированным массивом.declare -p foobar
правильно сообщаетdeclare -a foobar='()'
. Работает"${foobar:+1}"
только для переменных, не являющихся массивами?${foobar+1}
(без:
, я перевернул два примера в своем первоначальном ответе) правильно для массивов в bash, если ваше определение «определено» - «будет$foobar
работать подset -o nounset
». Если ваше определение отличается, bash немного странный. Смотрите мой обновленный ответ.0
индекс, ни ключ не определены так, как это верноa=()
, то${a+1}
ничего не возвращает правильно.defined
оператор.Test
мог сделать это; это не может быть сложно ( гм ....)Подводя итог ответу Жиля, я сформулировал следующие правила:
[[ -v foobar ]]
для переменных в версии Bash> = 4.2.declare -p foobar &>/dev/null
для переменных массива в версии Bash <4.2.(( ${foo[0]+1} ))
или(( ${bar[foo]+1} ))
для индексов массивов indexed (-a
) и keyed (-A
)declare
соответственно. Варианты 1 и 2 здесь не работают.источник
Я использую одну и ту же технику для всех переменных в bash, и она работает, например:
выходы:
в то время как
выходы:
источник
foobar=""
потом сообщу об этомfoobar is unset
. Нет, подожди, я заберу это обратно. Действительно только проверяет, является ли первый элемент пустым или нет, так что будет хорошей идеей, если вы знаете, что переменная НЕ является массивом, и вам нужна только пустота, а не определенность.