
У меня есть вывод 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
С использованиемjq:
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. Работает отлично и сэкономило несколько шагов.