Назад | Перейти на главную страницу

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

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

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

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

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

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

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

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

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

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 как строка ([@] в этом случае является простой буквальной строкой) и поэтому всегда отображается как ложь: "является буквальной строкой [@] пустой? »Ясно, что нет.

Я проверил это с 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

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

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

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

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

arr=("" "")

## Assuming default non-empty IFS
## TODO: also check for ${#arr[@]} -eq 0
concat="${arr[*]} "      # n-1 separators + 1 space + array elements
[[ "${#concat}" -ne "${#arr[@]}" ]]  && echo not empty array || echo empty array

тестовый пример с set -x

### a non-empty element
$ 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 empty elements
$ 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 вместо использования подоболочки, потому что вы не можете легко получить результат из подоболочки.

# inside a () subshell so we don't modify our own 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 "Array is empty"
else
    echo "Array is not empty"
fi

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

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

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