web-dev-qa-db-fra.com

Itération via un tableau JSON dans un script Shell

J'ai des données JSON comme suit dans le fichier 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"}
]

Je veux parcourir le tableau et extraire la valeur de chaque élément d'une boucle. J'ai vu jq . J'ai du mal à l'utiliser pour répéter. Comment puis je faire ça?

14
kosta

Utilisez simplement un filtre qui retournerait chaque élément du tableau. Ensuite, parcourez les résultats, assurez-vous simplement d'utiliser l'option de sortie compacte (-c) donc chaque résultat est placé sur une seule ligne et est traité comme un élément dans la boucle.

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

Essayez de le construire autour de cet exemple. (Source: site d'origine)

Exemple:

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

Une réponse antérieure dans ce fil a suggéré d'utiliser foreach de jq, mais cela peut être beaucoup plus compliqué que nécessaire, surtout compte tenu de la tâche indiquée. Plus précisément, foreach (et reduce) sont destinés à certains cas où vous devez accumuler des résultats.

Dans de nombreux cas (y compris dans certains cas où une étape de réduction est éventuellement nécessaire), il est préférable d'utiliser .[] Ou map(_). Ce dernier n'est qu'une autre façon d'écrire [. [] | _] donc si vous allez utiliser jq, il est vraiment utile de comprendre cela. [] crée simplement un stream de valeurs. Par exemple, [1,2,3] | .[] Produit un flux des trois valeurs.

Pour prendre un exemple simple de réduction de carte, supposons que vous souhaitiez trouver la longueur maximale d'un tableau de chaînes. Une solution serait [ .[] | length] | max.

1
peak

jq a une option de formatage Shell: @sh.

Vous pouvez utiliser les éléments suivants pour formater vos données json en tant que paramètres Shell:

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

La sortie ressemblera à:

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

Pour traiter chaque ligne, nous devons faire deux ou trois choses:

  • Définissez bash for-loop pour lire la ligne entière, plutôt que de vous arrêter au premier espace (comportement par défaut).
  • Supprimez les guillemets qui entourent chaque ligne afin que chaque valeur puisse être transmise en tant que paramètre à la fonction qui traite chaque ligne.

Pour lire la ligne entière à chaque itération de la boucle for bash, définissez la variable IFS, comme décrit dans cette réponse .

Pour supprimer les guillemets doubles, nous allons l'exécuter via l'interpréteur bash Shell en utilisant xargs:

stripped=$(echo $original | xargs echo)

Dans l'ensemble, nous avons:

#!/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