У меня есть массив, который заполняется различными сообщениями об ошибках во время выполнения моего скрипта.
Мне нужен способ проверить, не пусто ли оно в конце скрипта, и выполнить определенное действие, если оно есть.
Я уже пытался рассматривать его как обычный VAR и использовать -z для проверки, но это не похоже на работу. Есть ли способ проверить, является ли массив пустым или нет в Bash?
=
это строковый оператор. В этом случае это работает нормально, но-eq
вместо этого я бы использовал правильный арифметический оператор (на всякий случай, если бы я хотел переключиться на-ge
или-lt
и т. Д.).set -u
: "unbound variable" - если массив пуст.set -u;
foo=();
[ ${#foo[@]} -eq 0 ] && echo empty
, Если яunset foo
, то это печатаетfoo: unbound variable
, но это отличается: переменная массива вообще не существует, а не существует и является пустой.set -u
- если вы сначала объявили свою переменную, это работает отлично.Я обычно использую арифметическое расширение в этом случае:
источник
(( ${#a} ))
(длина первого элемента) также будет работать. Однако, это не удастсяa=('')
, тогда как(( ${#a[@]} ))
приведенный в ответе будет успешным.Вы также можете рассматривать массив как простую переменную. Таким образом, просто используя
или используя другую сторону
Проблема с этим решением является то , что если массив объявлен как это:
array=('' foo)
. Эти проверки будут сообщать массив как пустой, в то время как он явно нет. (спасибо @musiphil!)Использование
[ -z "$array[@]" ]
явно не является решением. Не указывая фигурные скобки пытается интерпретировать$array
как строка ([@]
в этом случае простая текстовая строка) , и поэтому всегда считаются ложными: «это символьная строка[@]
пустой?» Очевидно, нет.источник
[ -z "$array" ]
или[ -n "$array" ]
не работает. Попробуйтеarray=('' foo); [ -z "$array" ] && echo empty
, и он напечатает,empty
хотяarray
явно не пусто.[[ -n "${array[*]}" ]]
интерполирует весь массив как строку, которую вы проверяете на ненулевую длину. Если вы считаетеarray=("" "")
пустым, а не два пустых элемента, это может быть полезно.[[ -n " " ]]
является «истиной», что очень жаль. Ваш комментарий - именно то, что я хочу сделать.set -x
показывает, как он расширяется. Я думаю, что я не проверял этот комментарий перед публикацией. >. <Вы можете заставить его работать, установивIFS=''
(сохраните / восстановите его вокруг этого оператора), потому что"${array[*]}"
расширение разделяет элементы с первым символом IFS. (Или пробел, если не установлен). Но « Если IFS имеет значение null, параметры объединяются без промежуточных разделителей. » (Документы для позиционных параметров $ *, но я предполагаю, что то же самое для массивов).Я проверил это с
bash-4.4.0
:и
bash-4.1.5
:В последнем случае вам понадобится следующая конструкция:
для того, чтобы он не потерпел неудачу на пустом или неустановленном массиве. Это если ты делаешь так,
set -eu
как я обычно делаю. Это обеспечивает более строгую проверку ошибок. Из документов :Если вам это не нужно, не стесняйтесь пропустить
:+${array[@]}
часть.Также обратите внимание, что здесь важно использовать
[[
оператор, так как[
вы получаете:источник
-u
вами на самом деле стоит использовать${array[@]+"${array[@]}"}
cf stackoverflow.com/a/34361807/1237617@
, конечно же. Вы могли бы использовать*
расширение массива, как[ "${array[*]}" ]
, не так ли? Тем не менее,[[
также отлично работает. Поведение обоих из них для массива с несколькими пустыми строками немного удивительно. Оба[ ${#array[*]} ]
и[[ "${array[@]}" ]]
ложны дляarray=()
иarray=('')
но верны дляarray=('' '')
(две или более пустых строки). Если вы хотите, чтобы одна или несколько пустых строк дали всем true, вы можете использовать[ ${#array[@]} -gt 0 ]
. Если бы вы хотели, чтобы все они были ложными, вы могли бы//
их исключить.[ "${array[*]}" ]
, но если бы я столкнулся с таким выражением, мне было бы труднее понять, что оно делает. Так как[...]
действует в терминах строк по результату интерполяции. В отличие от того[[...]]
, который может быть в курсе того, что было интерполировано. То есть он может знать, что ему был передан массив.[[ ${array[@]} ]]
читается как «проверить, не является ли массив непустым», а[ "${array[*]}" ]
как «проверить, является ли результат интерполяции всех элементов массива непустой строкой».[ ${#array[*]} ]
, вы, вероятно, имели в виду[ "${array[*]}" ]
, так как первое верно для любого количества элементов. Потому что количество элементов всегда непустая строка. Что касается последнего с двумя элементами, то выражение в скобках расширяется до' '
непустой строки. Что касается[[ ${array[@]} ]]
, они просто думают (и это правильно), что любой массив из двух элементов не является пустым.Если вы хотите обнаружить массив с пустыми элементами ,
arr=("" "")
как пустой, так же, какarr=()
Вы можете вставить все элементы вместе и проверить, будет ли результат нулевой длины. (Построение плоской копии содержимого массива не идеально для производительности, если массив может быть очень большим. Но, надеюсь, вы не используете bash для таких программ ...)
Но
"${arr[*]}"
расширяется элементами, разделенными первым символомIFS
. Поэтому вам нужно сохранить / восстановить IFS и выполнитьIFS=''
эту операцию, или же проверить, что длина строки == # элементов массива - 1. (Массивn
элементов имеетn-1
разделители). Чтобы справиться с этим один за другим, проще всего дополнить конкатенацию 1контрольный пример с
set -x
К сожалению , это не выполняется для
arr=()
:[[ 1 -ne 0 ]]
. Таким образом, вам нужно сначала проверить наличие фактически пустых массивов.Или с
IFS=''
. Возможно, вы захотите сохранить / восстановить IFS вместо использования подоболочки, потому что вы не можете легко получить результат из подоболочки.пример:
делает работу с
arr=()
- это все-таки просто пустая строка.источник
[[ "${arr[*]}" = *[![:space:]]* ]]
, так как могу рассчитывать как минимум на одного не-WS персонажа.arr=(" ")
.В моем случае второго ответа было недостаточно, потому что могли быть пробелы. Я пришел вместе с:
источник
echo | wc
кажется излишне неэффективным по сравнению с использованием встроенных в оболочку.[ ${#errors[@]} -eq 0 ];
чтобы обойти проблему с пробелами? Я также предпочел бы встроенный.$#
расширяется до числа, и прекрасно работает даже послеopts+=("")
. напримерunset opts;
opts+=("");opts+=(" "); echo "${#opts[@]}"
и я получаю2
. Можете ли вы показать пример чего-то, что не работает?opts=("")
же, какopts=()
? Это не пустой массив, но вы можете проверить наличие пустого массива или пустого первого элемента с помощьюopts=("");
[[ "${#opts[@]}" -eq 0 || -z "$opts" ]] && echo empty
. Обратите внимание, что в вашем текущем ответе написано «без вариантов»opts=("" "-foo")
, что полностью обманчиво, и это воспроизводит это поведение. Можно было бы[[ -z "${opts[*]}" ]]
догадаться, чтобы интерполировать все элементы массива в плоскую строку, которая-z
проверяет ненулевую длину. Если проверки первого элемента достаточно,-z "$opts"
работает.Я предпочитаю использовать двойные скобки:
источник