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

13

У меня есть вывод 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.

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

JpaytonWPD
источник

Ответы:

17

Используя :

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

КОД:

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

ВЫХОД:

value2
value2_2
Жиль Квено
источник
Возможно ли сделать это из переменной вместо файла? Я стараюсь избегать лишнего доступа к файловой системе, если он мне не нужен. Как только я извлекаю этот массив, я заканчиваю вывод json.
JpaytonWPD
1
Вы что-нибудь пробовали?
Жиль Квено
2
jq . <<< "$json"это связано с оболочкой (bash), не специфично дляjq
Gilles Quenot
1
Отсутствующие скобки:arr=( $(...) )
Жиль Квено
4
Отличная jqкоманда, но, пожалуйста, не разбирайте вывод команды в массив с arr=( $(...) )(хотя это работает с примером ввода): она не работает так, как задумано, со встроенным или начальным / конечным пробелом и может привести к случайному сбою.
mklement0
5

Следующее действительно глючит:

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

Вместо этого используйте:

# GOOD (with bash 4.x+), but can't detect failure
readarray -t arr < <(curl -k "$url" | jq -r '.[].item2' )

... или даже лучше ...

# GOOD (with bash 3.x+), *and* has nonzero status if curl or jq fails
IFS=$'\n' read -r -d '' -a arr \
  < <(set -o pipefail; curl --fail -k "$url" | jq -r '.[].item2' && printf '\0')
Чарльз Даффи
источник
2

Благодаря спутнику я дошел до этого:

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

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

JpaytonWPD
источник
Этот код на самом деле немного глючит. Посмотрите, что произойдет, если у вас есть элемент результата *- он будет заменен списком файлов в вашем текущем каталоге.
Чарльз Даффи
1
Точно так же item2значение с пробелом в нем станет более чем одним элементом массива.
Чарльз Даффи
0

в качестве простой альтернативы, посмотрите на jtcинструмент (на https://github.com/ldn-softdev/jtc ), чтобы добиться того же (как в примере с jq):

bash $ arr=( $(jtc -w '<item2>l+0' file.json) )
bash $ printf '%s\n' "${arr[@]}"
"value2"
"value2_2"
bash $ 

Объяснение -wопции: угловые скобки <...>определяют поиск по всему json, суффикс lуказывает на поиск меток, а не значений, +0указывает на поиск всех вхождений (а не только первый).

Дмитрий Львович
источник
Та же ошибка, что и во всех arr=( $(jq ...) )ответах, поскольку содержимое разбивается на строки и расширяется глобусом для заполнения массива - это означает, что пробелы (не только новые строки) создают новые элементы, а элементы, которые выглядят как выражение глобуса, заменяются файлами, которые выражения совпадают.
Чарльз Даффи