Я пишу сценарий bash, который имеет set -u
, и у меня проблема с расширением пустого массива: кажется, что bash обрабатывает пустой массив как неустановленную переменную во время расширения:
$ set -u
$ arr=()
$ echo "foo: '${arr[@]}'"
bash: arr[@]: unbound variable
( declare -a arr
тоже не помогает.)
Обычным решением этой проблемы является использование ${arr[@]-}
вместо нее пустой строки вместо ("неопределенного") пустого массива. Однако это не очень хорошее решение, так как теперь вы не можете отличить массив с одной пустой строкой от пустого массива. (@ -разложение особенное в Баше, он расширяется "${arr[@]}"
в "${arr[0]}" "${arr[1]}" …
, что делает его идеальным инструментом для построения командной строки.)
$ countArgs() { echo $#; }
$ countArgs a b c
3
$ countArgs
0
$ countArgs ""
1
$ brr=("")
$ countArgs "${brr[@]}"
1
$ countArgs "${arr[@]-}"
1
$ countArgs "${arr[@]}"
bash: arr[@]: unbound variable
$ set +u
$ countArgs "${arr[@]}"
0
Есть ли способ обойти эту проблему, кроме проверки длины массива в if
(см. Образец кода ниже) или отключения -u
настройки для этого короткого фрагмента?
if [ "${#arr[@]}" = 0 ]; then
veryLongCommandLine
else
veryLongCommandLine "${arr[@]}"
fi
Обновление: удален bugs
тег из-за объяснения ikegami.
"${arr[@]}"
. Я что-то упускаю? Насколько я могу судить, он работает по крайней мере в5.x
.Согласно документации,
Нижнему индексу не присвоено значение, поэтому массив не задан.
Но хотя документация предполагает, что ошибка здесь уместна, с 4.4 .
Существует условное выражение, которое вы можете использовать inline для достижения желаемого в более старых версиях: Используйте
${arr[@]+"${arr[@]}"}
вместо"${arr[@]}"
.Протестировано с помощью bash 4.2.25 и 4.3.11.
источник
[@]+
самом деле делает и почему второй${arr[@]}
не вызовет несвязанную ошибку.${parameter+word}
только расширяется,word
еслиparameter
не сброшено.${arr+"${arr[@]}"}
короче и, кажется, работает так же хорошо.unset arr
,arr[1]=a
,args ${arr+"${arr[@]}"}
Противargs ${arr[@]+"${arr[@]}"}
+
раскрытие не происходит (а именно, пустой массив), расширение заменяется ничем , что является именно тем, до чего расширяется пустой массив.:+
небезопасен, поскольку он также обрабатывает одноэлементный('')
массив как незаданный и аналогично расширяется до нуля, теряя значение.Принятый ответ @ ikegami слегка неверен! Правильное заклинание
${arr[@]+"${arr[@]}"}
:источник
bash-4.4.23
:arr=('') && countArgs "${arr[@]:+${arr[@]}}"
производит1
. Но${arr[@]+"${arr[@]}"}
форма позволяет различать пустое / непустое значение, добавляя / не добавляя двоеточие.arr=('') && countArgs ${arr[@]:+"${arr[@]}"}
->0
,arr=('') && countArgs ${arr[@]+"${arr[@]}"}
->1
.Оказывается, обработка массивов была изменена в недавно выпущенном (2016/09/16) bash 4.4 (например, доступном в Debian stretch).
Теперь расширение пустых массивов не выдает предупреждения
источник
bash-4.4.12
"${arr[@]}"
хватит.это может быть еще один вариант для тех, кто предпочитает не дублировать arr [@] и может иметь пустую строку
тестировать:
источник
for
это приведет к единственной пустой строке, когда массив undefined / defined-as-empty, где, как вам может понадобиться, тело цикла не запускать, если массив не определен.${arr[@]+"${arr[@]}"}
, правильно сохраняет состояние пустого массива.@ ikegami ответ правильный, но я считаю синтаксис
${arr[@]+"${arr[@]}"}
ужасным. Если вы используете длинные имена переменных массива, он начинает выглядеть как спагетти быстрее, чем обычно.Попробуйте вместо этого:
Похоже, оператор среза массива Bash очень снисходительный.
Так почему же Bash так усложнил работу с крайним случаем массивов? Вздох. Я не могу гарантировать, что ваша версия допускает такое злоупотребление оператором среза массива, но для меня это отлично работает.
Предостережение: я использую
GNU bash, version 3.2.25(1)-release (x86_64-redhat-linux-gnu)
Ваш пробег может отличаться.источник
"${arr[@]:0}"
дает-bash: arr[@]: unbound variable
.arr=("_dummy_")
и${arr[@]:1}
везде использовать расширение . Это упоминается в других ответах, касающихся дозорных значений.Действительно "интересная" нестыковка.
Более того,
Хотя я согласен с тем, что текущее поведение не может быть ошибкой в том смысле, который объясняет @ikegami, ИМО, мы могли бы сказать, что ошибка заключается в самом определении («набора») и / или в том факте, что оно применяется непоследовательно. В предыдущем абзаце на странице руководства говорится
что полностью соответствует тому, что говорится о расширении позиционных параметров в
"$@"
. Не то чтобы нет других несоответствий в поведении массивов и позиционных параметров ... но для меня нет намека на то, что эта деталь должна быть несовместимой между ними.Продолжая,
Так
arr[]
не настолько ли несвязан, что мы не можем получить количество его элементов (0) или (пустой) список его ключей? Для меня это разумно и полезно - кажется, единственным исключением является расширение${arr[@]}
(и${arr[*]}
).источник
Я дополняющий на @ Икегах (принято) и @ kevinarpe - х (также хорошо) ответы.
Вы можете
"${arr[@]:+${arr[@]}}"
обойти проблему. Правая часть (т.е. после:+
) предоставляет выражение, которое будет использоваться в случае, если левая часть не определена / не задана.Синтаксис непонятен. Обратите внимание, что правая часть выражения подвергнется расширению параметров, поэтому особое внимание следует уделить согласованному заключению в кавычки.
Как упоминает @kevinarpe, менее загадочным синтаксисом является использование нотации среза массива
${arr[@]:0}
(в версиях Bash>= 4.4
), которая расширяется до всех параметров, начиная с индекса 0. Это также не требует такого большого повторения. Это расширение работает независимо от тогоset -u
, поэтому вы можете использовать его в любое время. На странице руководства говорится (в разделе « Расширение параметров» ):Это пример, предоставленный @kevinarpe, с альтернативным форматированием для демонстрации вывода:
Это поведение зависит от версии Bash. Вы также могли заметить, что оператор длины
${#arr[@]}
всегда будет оценивать0
пустые массивы независимо от тогоset -u
, не вызывая «ошибку несвязанной переменной».источник
:0
идиома не работает в Bash 4.2, так что это небезопасный подход. Смотрите мой ответ .Вот несколько способов сделать что-то вроде этого, один с использованием часовых, а другой с использованием условных добавлений:
источник
Интересная нестыковка; это позволяет вам определить что-то, что «не считается установленным», но отображается в выводе
declare -p
ОБНОВЛЕНИЕ: как уже упоминалось, исправлено в версии 4.4, выпущенной после публикации этого ответа.
источник
echo ${arr[@]}
(но до Bash 4.4 вы все равно будете видеть ошибку).echo $arr[@]
себя, вы бы увидели, что сообщение об ошибке другое.Наиболее простой и совместимый способ выглядит так:
источник