it-swarm.com.ru

Итерация по массиву JSON в сценарии Shell

У меня есть данные JSON, как показано в файле data.json

[
  {"original_name":"pdf_convert","changed_name":"pdf_convert_1"},
  {"original_name":"video_encode","changed_name":"video_encode_1"},
  {"original_name":"video_transcode","changed_name":"video_transcode_1"}
]

Я хочу перебрать массив и извлечь значение для каждого элемента в цикле. Я видел JQ . Мне сложно использовать его для повторения. Как я могу это сделать?

6
user3288346

Просто используйте фильтр, который будет возвращать каждый элемент в массиве. Затем зациклите результаты, просто убедитесь, что вы используете опцию компактного вывода (-c), чтобы каждый результат помещался в одну строку и обрабатывался как один элемент в цикле.

jq -c '.[]' input.json | while read i; do
    # do stuff with $i
done
12
Jeff Mercado

Попробуйте построить это вокруг этого примера. (Источник: оригинальный сайт)

Пример:

jq '[foreach .[] as $item ([[],[]]; if $item == null then [[],.[0]]     else [(.[0] + [$item]),[]] end; if $item == null then .[1] else empty end)]'

Input [1,2,3,4,null,"a","b",null]

Output [[1,2,3,4],["a","b"]]

1
touchStone

В предыдущем ответе в этой теме предлагалось использовать foreach из jq, но это может быть гораздо сложнее, чем нужно, особенно с учетом поставленной задачи. В частности, foreachreduce) предназначены для определенных случаев, когда вам нужно накапливать результаты. 

Во многих случаях (в том числе в некоторых случаях, когда в конечном итоге необходим шаг сокращения), лучше использовать .[] или map(_). Последний просто еще один способ написания [. [] | _] поэтому, если вы собираетесь использовать jq, очень полезно это понимать. [] просто создает stream of values ​​. Например, [1,2,3] | .[] создает поток из трех значений.

В качестве простого примера сокращения карты предположим, что вы хотите найти максимальную длину массива строк. Одним из решений будет [ .[] | length] | max.

1
peak

jq имеет опцию форматирования оболочки: @sh.

Для форматирования данных json в качестве параметров оболочки вы можете использовать следующее:

cat data.json | jq '. | map([.original_name, .changed_name])' | jq @sh

Вывод будет выглядеть так:

"'pdf_convert' 'pdf_convert_1'"
"'video_encode' 'video_encode_1'",
"'video_transcode' 'video_transcode_1'"

Для обработки каждой строки нам нужно сделать пару вещей:

  • Установите цикл for bash для чтения всей строки, а не останавливайтесь на первом пробеле (поведение по умолчанию).
  • Удалите вмещающие двойные кавычки из каждой строки, чтобы каждое значение можно было передать в качестве параметра функции, которая обрабатывает каждую строку.

Чтобы прочитать всю строку на каждой итерации цикла bash for, установите переменную IFS, как описано в этот ответ .

Чтобы убрать двойные кавычки, мы запустим его через интерпретатор bash Shell, используя xargs:

stripped=$(echo $original | xargs echo)

Собрав все это вместе, мы имеем:

#!/bin/bash

function processRow() {
  original_name=$1
  changed_name=$2

  # TODO
}

IFS=$'\n' # Each iteration of the for loop should read until we find an end-of-line
for row in $(cat data.json | jq '. | map([.original_name, .changed_name])' | jq @sh)
do
  # Run the row through the Shell interpreter to remove enclosing double-quotes
  stripped=$(echo $row | xargs echo)

  # Call our function to process the row
  # eval must be used to interpret the spaces in $stripped as separating arguments
  eval processRow $stripped
done
unset IFS # Return IFS to its original value
0
Mashmagar