Вопрос: Проверьте, нет ли массива в Bash


У меня есть массив, который заполняется различными сообщениями об ошибках по мере запуска моего сценария.

Мне нужен способ проверить, не пуст ли он в конце скрипта и принять конкретное действие, если оно есть.

Я уже пробовал рассматривать его как обычный VAR и использовать -z, чтобы проверить его, но это, похоже, не работает. Есть ли способ проверить, является ли массив пустым или нет в Bash?

благодаря


76
2018-02-11 03:59


Источник




Ответы:


Предположим, что ваш массив $errors, просто проверьте, равна ли количество элементов нулю.

if [ ${#errors[@]} -eq 0 ]; then
    echo "No errors, hooray"
else
    echo "Oops, something went wrong..."
fi

104
2018-02-11 04:10



Обратите внимание, что = является строковым оператором. В этом случае это нормально работает, но я бы использовал правильный арифметический оператор -eq вместо этого (на всякий случай я хочу переключиться на -ge или -lt, и т.д.). - musiphil
Не работает с set -u: «unbound variable» - если массив пуст. - Igor
@Igor: Работает для меня в Bash 4.4. set -u;  foo=();  [ ${#foo[@]} -eq 0 ] && echo empty, Если я unset foo, то он печатает foo: unbound variable, но это другое: переменная массива вообще не существует, а не существует и является пустой. - Peter Cordes
Также тестируется в Bash 3.2 (OSX) при использовании set -u - Пока вы сначала указали свою переменную, это работает отлично. - zeroimpl


Вы можете также рассматривать массив как простую переменную. Таким образом, просто используя

if [ -z "$array" ]; then
    echo "Array empty"
else
    echo "Array non empty"
fi

или с другой стороны

if [ -n "$array" ]; then
    echo "Array non empty"
else
    echo "Array empty"
fi

Проблема с этим решением заключается в том, что если массив объявлен следующим образом: array=('' foo), Эти проверки будут сообщать массив как пустой, в то время как это явно не так. (спасибо @musiphil!)

С помощью [ -z "$array[@]" ] явно не является решением. Не указывая фигурные скобки, попытки интерпретировать $array как строка ([@] в этом случае простая строка литерала) и поэтому всегда сообщается как false: "- это буквальная строка [@] пустой? »Очевидно, нет.


4
2018-06-23 10:29



[ -z "$array" ] или [ -n "$array" ] не работает. Пытаться array=('' foo); [ -z "$array" ] && echo empty, и он будет печатать empty хотя array явно не пусто. - musiphil
[[ -n "${array[*]}" ]] интерполирует весь массив как строку, которую вы проверяете на ненулевую длину. Если вы считаете array=("" "") быть пустым, а не иметь два пустых элемента, это может быть полезно. - Peter Cordes


Я проверил его с помощью bash-4.4.0:

#!/usr/bin/env bash
set -eu
check() {
    if [[ ${array[@]} ]]; then
        echo not empty
    else
        echo empty
    fi
}
check   # empty
array=(a b c d)
check   # not empty
array=()
check   # empty

а также bash-4.1.5:

#!/usr/bin/env bash
set -eu
check() {
    if [[ ${array[@]:+${array[@]}} ]]; then
        echo non-empty
    else
        echo empty
    fi
}
check   # empty
array=(a b c d)
check   # not empty
array=()
check   # empty

В последнем случае вам понадобится следующая конструкция:

${array[@]:+${array[@]}}

чтобы он не прерывался при пустом или неустановленном массиве. Вот если вы это сделаете set -eu как обычно. Это обеспечивает более строгую проверку ошибок. Из документы:

-e

Выйдите немедленно, если конвейер (см. «Трубопроводы»), который может состоять из одной простой команды (см. «Простые команды»), списка (см. Списки) или составной команды (см. Составные команды) возвращает ненулевой статус. Оболочка не выходит, если неудавшаяся команда является частью списка команд сразу после некоторого времени или до тех пор, пока ключевое слово, часть теста в выражении if, не будет частью любой команды, выполняемой в && или || кроме команды, следующей за окончательной командой && или ||, любую команду в конвейере, но последнюю, или если возвращаемый статус команды перевернут с помощью!. Если составная команда, отличная от подоболочки, возвращает ненулевой статус, потому что команда была неудачной, а -e игнорировалась, оболочка не выходила. Ловушка ERR, если установлена, выполняется до выхода оболочки.

Этот параметр применяется к среде оболочки и каждой среде подсетей отдельно (см. «Среда выполнения команд»), и может привести к тому, что подоболочки будут завершены до выполнения всех команд в подоболочке.

Если составная команда или функция оболочки выполняются в контексте, где -e игнорируется, ни одна из команд, выполняемых в составной команде или корпусе функции, не будет затронута параметром -e, даже если -e задано, и команда возвращает статус отказа. Если составная команда или функция оболочки задают -e при выполнении в контексте, где -e игнорируется, этот параметр не будет иметь никакого эффекта до тех пор, пока не будет завершена составная команда или команда, содержащая вызов функции.

-u

Устраните переменные и параметры, отличные от специальных параметров «@» или «*», как ошибку при выполнении расширения параметра. Сообщение об ошибке будет записано в стандартную ошибку, и неинтерактивная оболочка завершит работу.

Если вам это не нужно, не стесняйтесь пропустить :+${array[@]} часть.

Также обратите внимание, что необходимо использовать [[ оператора здесь, с [ Вы получаете:

$ cat 1.sh
#!/usr/bin/env bash
set -eu
array=(a b c d)
if [ "${array[@]}" ]; then
    echo non-empty
else
    echo empty
fi

$ ./1.sh
_/1.sh: line 4: [: too many arguments
empty

1
2017-12-04 14:58



С -u вы должны фактически использовать ${array[@]+"${array[@]}"} сравни stackoverflow.com/a/34361807/1237617 - Jakub Bochenski
@JakubBochenski Какую версию bash вы говорите? gist.github.com/x-yuri/d933972a2f1c42a49fc7999b8d5c50b9 - x-yuri


В моем случае второй ответ было недостаточно, потому что могут быть пробелы. Я пришел с:

if [ "$(echo -ne ${opts} | wc -m)" -eq 0 ]; then
  echo "No options"
else
  echo "Options found"
fi

0
2018-02-17 19:54



echo | wc кажется бесполезно неэффективным по сравнению с использованием встроенных оболочек. - Peter Cordes
Не уверен, что я понимаю @PeterCordes, могу ли я изменить второй вариант ответов, [ ${#errors[@]} -eq 0 ]; для того, чтобы обойти проблему с пробелами? Я также предпочел бы встроенный. - Micha
Как именно пробел вызывает проблему? $# расширяется до числа и отлично работает даже после opts+=(""), например unset opts;  opts+=("");opts+=(" "); echo "${#opts[@]}" и я получаю 2, Можете ли вы показать пример того, что не работает? - Peter Cordes
Это давно. IIRC источник происхождения всегда печатается как минимум "". Таким образом, для opts = "" или opts = ("") мне понадобилось 0, а не 1, игнорируя пустую строку новой строки или пустую строку. - Micha
Хорошо, так что вам нужно лечить opts=("") такой же как opts=()? Это не пустой массив, но вы можете проверить пустой массив или пустой первый элемент с opts=("");  [[ "${#opts[@]}" -eq 0 || -z "$opts" ]] && echo empty, Обратите внимание, что в вашем текущем ответе «нет параметров» для opts=("" "-foo"), что является полностью фиктивным, и это воспроизводит это поведение. Ты мог [[ -z "${opts[*]}" ]] Я предполагаю, что интерполяция всех элементов массива в плоскую строку, которая -z проверяет ненулевую длину.  Если проверка первого элемента достаточно, -z "$opts" работает. - Peter Cordes


Я предпочитаю использовать двойные скобки:

if [[ !${array[@]} ]]
then
    echo "Array is empty"
else
    echo "Array is not empty"
fi

Двойные скобки: https://stackoverflow.com/questions/669452/is-preferable-over-in-bash


0
2017-11-04 06:40





Обычно я использую арифметическое расширение:

if (( ${#a[@]} )); then
    echo not empty
fi

0
2017-08-02 06:04