парсить одно поле из массива JSON в массив bash

парсить одно поле из массива JSON в массив bash

У меня есть вывод JSON, содержащий список объектов, хранящихся в переменной. (Возможно, я не совсем правильно выразился)

[
  {
    "item1": "value1",
    "item2": "value2",
    "sub items": [
      {
        "subitem": "subvalue"
      }
    ]
  },
  {
    "item1": "value1_2",
    "item2": "value2_2",
    "sub items_2": [
      {
        "subitem_2": "subvalue_2"
      }
    ]
  }
]

Мне нужны все значения item2 в массиве для запуска bash-скрипта на Ubuntu 14.04.1.

Я нашел кучу способов поместить весь результат в массив, а не только нужные мне элементы.

решение1

С использованием:

readarray arr < <(jq '.[].item2' json)
printf '%s\n' "${arr[@]}"

Если вам нужен более жесткий способ:

readarray -td '' arr

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

Выход:

value2
value2_2

Проверять:

Процесс Подстановка >(command ...)или <(...)заменяется временным именем файла. Запись или чтение этого файла приводит к тому, что байты передаются команде внутри. Часто используется в сочетании с перенаправлением файлов: cmd1 2> >(cmd2). Смотритеhttp://mywiki.wooledge.org/ProcessSubstitution http://mywiki.wooledge.org/BashFAQ/024

решение2

На самом деле, следующее является ошибкой:

# BAD: Output line of * is replaced with list of local files; can't deal with whitespace
arr=( $( curl -k "$url" | jq -r '.[].item2' ) )

Если у вас bash 4.4 или более новая версия, доступен лучший из всех вариантов:

# BEST: Supports bash 4.4+, with failure detection and newlines in data
{ readarray -t -d '' arr && wait "$!"; } < <(
  set -o pipefail
  curl --fail -k "$url" | jq -j '.[].item2 | (., "\u0000")'
)

...тогда как в bash 4.0 вы можете добиться краткости за счет обнаружения ошибок и поддержки буквальной новой строки:

# OK (with bash 4.0), but can't detect failure and doesn't support values with newlines
readarray -t arr < <(curl -k "$url" | jq -r '.[].item2' )

...или совместимость с bash 3.x и обнаружение сбоев, но без поддержки новой строки:

# OK: Supports bash 3.x; no support for newlines in values, but can detect failures
IFS=$'\n' read -r -d '' -a arr < <(
  set -o pipefail
  curl --fail -k "$url" | jq -r '.[].item2' && printf '\0'
)

...или совместимость с bash 3.x и поддержка новой строки, но без обнаружения сбоев:

# OK: Supports bash 3.x and supports newlines in values; does not detect failures
arr=( )
while IFS= read -r -d '' item; do
  arr+=( "$item" )
done < <(curl --fail -k "$url" | jq -j '.[] | (.item2, "\u0000")')

решение3

Используйте jqдля создания оператора оболочки, который вы оцениваете:

eval "$( jq -r '@sh "arr=( \([.[].item2]) )"' file.json )"

Учитывая документ JSON в вашем вопросе, вызов jqвернет строку

arr=( 'value2' 'value2_2' )

которая затем оценивается вашей оболочкой. Оценка этой строки создаст именованный массив arrс двумя элементами value2и value2_2:

$ eval "$( jq -r '@sh "arr=( \([.[].item2]) )"' file.json )"
$ printf '"%s"\n' "${arr[@]}"
"value2"
"value2_2"

Оператор @shзаботится jqо том, чтобы правильно указать данные для оболочки.

В качестве альтернативы вынесите arr=( ... )часть из jqвыражения:

eval "arr=( $( jq -r '@sh "\([.[].item2])"' file.json ) )"

Теперь jqгенерируется только указанный список элементов, который затем вставляется arr=( ... )и оценивается.

Если вам необходимо прочитать данные из curlкоманды, используйте curl ... | jq -r ...вместо jq -r ... file.jsonв командах выше.

решение4

Благодаря sputnick я добрался до этого:

arr=( $(curl -k https://localhost/api | jq -r '.[].item2') )

JSON, который у меня есть, — это вывод API. Все, что мне нужно было сделать, это удалить аргумент файла и передать |вывод curl в jq. Работает отлично и сэкономило несколько шагов.

Связанный контент