Проверьте, пустой ли массив в Bash

Вопрос или проблема

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

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

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

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

if [ ${#errors[@]} -eq 0 ]; then
    echo "Нет ошибок, ура"
else
    echo "Упс, что-то пошло не так..."
fi

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

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

Вы также можете рассматривать массив как простую переменную. В таком случае просто используйте:

if [ -z "$array" ]; then
    echo "Массив пуст"
else
    echo "Массив непуст"
fi

или используя другую проверку:

if [ -n "$array" ]; then
    echo "Массив непуст"
else
    echo "Массив пуст"
fi

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

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

Я проверил это на bash-4.4.0:

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

и на bash-4.1.5:

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

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

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

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

-e

Выходите немедленно, если конвейер (см. Конвейеры), который может состоять из одной простой команды (см. Простые команды), списка (см. Списки) или составной команды (см. Составные команды) возвращает ненулевой статус. Шелл не выходит, если команда, которая не удалась, является частью командного списка, непосредственно следующего за ключевым словом while или until, частью теста в операторе if, частью любой команды, выполняемой в списке && или ||, за исключением команды, следующей после последнего && или ||, любой команды в конвейере, кроме последней, или если возвращаемый статус команды инвертируется с помощью !. Если составная команда, отличная от подсистемы, возвращает ненулевой статус, потому что команда не удалась, в то время как -e игнорировался, шелл не выходит. Операция trap на 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: [: слишком много аргументов
empty

Если вы хотите обнаружить массив с пустыми элементами, такими как arr=("" ""), как пустой, так же как arr=()

Вы можете объединить все элементы и проверить, равен ли результат нулевой длине. (Создание уплощенной копии содержимого массива не идеально для производительности, если массив может быть очень большим. Но, надеюсь, вы не используете bash для таких программ…)

Но "${arr[*]}" расширяется с элементами, разделенными первым символом IFS. Поэтому вам нужно сохранить/восстановить IFS и установить IFS='', чтобы это работало, или проверить, что длина строки == количеству элементов массива – 1. (Массив с n элементами имеет n-1 разделителей). Чтобы справиться с этим несовпадением, проще всего добавить 1 к конкатенации.

arr=("" "")

## Предполагая, что IFS по умолчанию не пуст
## TODO: также проверьте ${#arr[@]} -eq 0
concat="${arr[*]} "      # n-1 разделителей + 1 пробел + элементы массива
[[ "${#concat}" -ne "${#arr[@]}" ]]  && echo not empty array || echo empty array

тестовый случай с set -x

### не пустой элемент
$ arr=("" "x")
  + arr=("" "x")
$ concat="${arr[*]} ";  [[ "${#concat}" -ne "${#arr[@]}" ]] && echo not empty array || echo empty array
  + concat=" x "
  + [[ 3 -ne 2 ]]
  + echo not empty array
not empty array

### 2 пустых элемента
$ arr=("" "")
  + arr=("" "")
$ concat="${arr[*]} ";  [[ "${#concat}" -ne "${#arr[@]}" ]] && echo not empty array || echo empty array
  + concat="  "
  + [[ 2 -ne 2 ]]
  + echo empty array
empty array

К сожалению, это не сработает для arr=(): [[ 1 -ne 0 ]]. Поэтому вам нужно будет сначала отдельно проверить на пустые массивы.


Или с IFS=''. Наверное, вам следует сохранить/восстановить IFS вместо использования подсистемы, потому что вы не можете легко получить результат из подсистемы.

# внутри () подсистемы, чтобы мы не изменяли свой собственный IFS
(IFS='' ; [[ -n "${arr[*]}" ]] && echo not empty array || echo empty array)

пример:

$ arr=("" "")
$ (IFS='' ; [[ -n "${arr[*]}" ]] && echo not empty array || echo empty array)
   + IFS=
   + [[ -n '' ]]
   + echo empty array
empty array

сработает для arr=() – это просто пустая строка.

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

if [[ ! ${array[@]} ]]
then
    echo "Массив пуст"
else
    echo "Массив не пуст"
fi

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

Отредактировано, чтобы работать как ожидалось

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

if [ "$(echo -ne ${opts} | wc -m)" -eq 0 ]; then
  echo "Нет опций"
else
  echo "Опции найдены"
fi

К сожалению, я пока не могу прокомментировать – относительно @Nick Tsai – это не сработало для меня, но следующее:

if [[ ! ${arrayName[*]} ]]; then ...
if [[ ! ${arrayOne[*]} && ! ${arrayTwo[*]} ]]; then ...

размер пробелов важен для успешной работы!

arr=()
#arr=(1 2 3)
if [[ -v arr ]];then
    echo "не пуст"
else
    echo "пуст"
fi

Вы также можете воспользоваться jq, если он установлен в вашей системе:

if [[ $(echo $array | jq -r 'length') -eq 0 ]]
then
    echo "Массив пуст"
else
    echo "Массив не пуст"
fi

Ответ или решение

Для проверки, является ли массив пустым в Bash, вы можете воспользоваться несколькими методами. Ниже приведены самые распространенные и эффективные способы для достижения этой цели.

Способ 1: Использование длины массива

Самый прямой и надежный способ — проверить количество элементов в массиве. Вы можете использовать следующий код:

if [ ${#errors[@]} -eq 0 ]; then
    echo "Ошибка отсутствует, ура!"
else
    echo "Ой, что-то пошло не так..."
fi

Здесь ${#errors[@]} возвращает количество элементов в массиве errors.

Способ 2: Использование арифметического расширения

Можно также использовать арифметическое расширение, чтобы проверить, является ли массив пустым:

if (( ${#errors[@]} )); then
    echo "Массив не пустой"
else
    echo "Массив пустой"
fi

Этот вариант проще, так как не требует написания дополнительных символов.

Способ 3: Использование конструкции [[

Использование конструкции [[ предлагает удобный способ для проверки массива:

if [[ ! ${errors[@]} ]]; then
    echo "Массив пустой"
else
    echo "Массив не пустой"
fi

Способ 4: Проверка на наличие пустых элементов

Если вам необходимо определить, является ли массив пустым или содержит только пустые строки, вы можете выполнить соединение элементов массива:

arr=("" "")  # Массив с пустыми строками

concat="${arr[*]} "  # Конкатенация элементов массива
[[ "${#concat}" -ne "${#arr[@]}" ]] && echo "Массив не пустой" || echo "Массив пустой"

Здесь, если длина конкатенированного результата отличается от количества элементов в массиве, это означает, что в массиве есть ненулевые элементы.

Способ 5: Использование jq для JSON-массивов

Если ваш массив представлен в формате JSON, вы можете использовать утилиту jq для проверки его длины:

if [[ $(echo "${array}" | jq -r 'length') -eq 0 ]]; then
    echo "Массив пустой"
else
    echo "Массив не пустой"
fi

Заключение

Эти методы позволяют эффективно проверять, является ли массив пустым в Bash. Подходите к выбору метода в зависимости от ваших конкретных нужд и контекста использования скрипта. Если массив может содержать пустые строки, обязательно учтите дополнительные проверки, чтобы корректно обработать эти случаи.

Оцените материал
Добавить комментарий

Капча загружается...