Проблема с назначением массива bash readarray -t, когда jq выводит пустую строку

Во-первых - очень простой код:

#!/bin/bash

a_string="string"
readarray -t a <<<"$a_string"
echo "${a[@]}"
echo "${#a[@]}"

# read empty string into array
emptystring=""
readarray -t b <<<"$emptystring"
echo "${b[@]}"
echo "${#b[@]}"

# and now - empty array
c=()
echo "${c[@]}"
echo "${#c[@]}"

И вывод

string
1

1

0

Таким образом, чтение пустой строки в массив bash с readarray -t показывает длину массива как 1. И только длина действительно пустого массива равна 0.

Мой вопрос - почему это происходит?

В моем случае - вот фрагмент из моего скрипта - выполнить вызов API, получить JSON и применить фильтр jq:

URL_list_json=$(curl -s --request GET "$curl_request" --header "Authorization: Bearer ${token}")
readarray -t URL_names <<<"$(echo "$URL_list_json" | jq -r '.[].Body.monitors[].name')"

Вот пример JSON, который возвращает пустой массив из вызова jq:

[
    {
        "Header": {
            "Region": "dc1",
            "Tenant": "tenant1",
            "Stage": "test"
        },
        "Body": {
            "monitors": []
        }
    }
]

и вот JSON с заполненным содержимым, возвращающим непустой массив из вызова jq:

[
    {
        "Header": {
            "Region": "dc2",
            "Tenant": "tenant2",
            "Stage": "qa"
        },
        "Body": {
            "monitors": [
                {
                    "enabled": true,
                    "entityId": "TEST-674BA97E74FC74AA",
                    "name": "production.example.com"
                },
                {
                    "enabled": false,
                    "entityId": "TEST-3E2D23438A973D1E",
                    "name": "test.example.com"
                }
            ]
        }
    }
]

Мне нужно проверить, пуст ли массив URL_names. Если не пусто, то перебрать содержимое. Если пусто - означает, что jq не вернул никаких результатов - белый, чтобы войти и двигаться дальше.

Если я использую if ((${#URL_names[@]})) как средство проверки, пуст ли массив или нет, он вернет 1, даже если в массиве есть только пустая строка из вызова jq, поэтому эта логика терпит неудачу.

Каковы альтернативные подходы к решению описанного выше случая?

Я могу назначить вывод фильтра jq строке, а затем использовать оператор if, чтобы проверить, пуста ли строка, и если она не пуста, то присвоить строку массиву, но это вводит дополнительные элементы. При таком подходе я могу полностью отказаться от использования массивов — я надеялся использовать для этой задачи только массивы.

Спасибо


person Invisible999    schedule 27.09.2020    source источник
comment
an empty string это не пустая строка, она имеет одну новую строку.   -  person KamilCuk    schedule 27.09.2020
comment
Вы пробовали это с read -a вместо mapfile/readarray?   -  person Rfroes87    schedule 27.09.2020


Ответы (1)


почему это происходит?

Потому что он читает одну строку. Из здесь документ руководства по bash:

[н]‹‹‹ слово

[...] Результат предоставляется в виде одной строки, с добавленной новой строкой, для команды на ее стандартном вводе (или файловом дескрипторе n, если n указано).

Поскольку есть новая строка, readarray читает одну пустую строку. Вы можете сделать:

readarray -t b < <(printf "%s" "$emptystring")

Заметки:

  • echo "$var" не является предпочтительным. Сделайте printf "%s" "$var" в оболочке posix, сделайте <<<"$var" в bash (и вас не волнует лишняя новая строка).
  • <<<"$(...)" всегда выглядит странно - <<< все равно должен выделять подоболочку. Сделайте < <(...).
  • Вы хотите сделать: readarray -t URL_names < <(<<<"$URL_list_json" jq -r '.[].Body.monitors[].name')
  • Если вы хотите проверить, пуст ли массив, проверьте это в jq. Я вижу упр. jq --exit-status '.[].Body.monitors[].name' и просто проверяю статус выхода.
person KamilCuk    schedule 27.09.2020
comment
Спасибо за комментарий. Я не думаю, что статус выхода jq будет работать в моем случае, потому что все, что я хочу видеть, это результат вызова jq и принимать решения на основе этого результата. Если мне нужно сначала проверить статус выхода, а затем на основе статуса выхода сделать еще один вызов jq - это дублирование усилий. - person Invisible999; 28.09.2020
comment
Кажется, эта конструкция readarray -t URL_names < <( jq -r '. [].Body.monitors[].name' <<<"$URL_list_json" | sort) должна выполнять эту работу. - person Invisible999; 28.09.2020
comment
If I need to check exit status first and then based on the exit status to do another jq call - that's a duplication of efforts ? просто if ! output=$(jq something); then echo no data; fi - person KamilCuk; 28.09.2020