web-dev-qa-db-fra.com

Analyser JSON dans un tableau dans un script Shell

J'essaie d'analyser un objet JSON dans un script Shell dans un tableau.

par exemple: [Amanda, 25, http://mywebsite.com]

Le JSON ressemble à:

{
  "name"       : "Amanda", 
  "age"        : "25",
  "websiteurl" : "http://mywebsite.com"
}

Je ne veux pas utiliser de bibliothèques, il serait préférable d'utiliser une expression régulière ou grep. J'ai fait:

myfile.json | grep name

Cela me donne "nom": "Amanda". Je pourrais le faire en boucle pour chaque ligne du fichier et l'ajouter à un tableau, mais je n'ai besoin que du côté droit et non de la ligne entière.

11
unconditionalcoder

Si vous ne pouvez vraiment pas utiliser un analyseur JSON approprié tel que jq[1] , essayez une solution basée sur awk:

Bash 4.x:

readarray -t values < <(awk -F\" 'NF>=3 {print $4}' myfile.json)

Bash 3.x:

IFS=$'\n' read -d '' -ra values < <(awk -F\" 'NF>=3 {print $4}' myfile.json)

Cela stocke toutes les propriétés valeurs dans le tableau Bash ${values[@]}, Que vous pouvez inspecter avec
declare -p values.

Ces solutions ont des limites:

  • chaque propriété doit être sur sa propre ligne,
  • toutes les valeurs doivent être entre guillemets,
  • les guillemets d'échappement doubles intégrés ne sont pas pris en charge.

Toutes ces limitations renforcent la recommandation d'utiliser un analyseur JSON approprié.


Remarque: Les solutions alternatives suivantes utilisent la commande Bash 4.x + readarray -t values, Mais elles fonctionnent également avec l'alternative Bash 3.x, IFS=$'\n' read -d '' -ra values.

grep + cut combinaison : une seule commande grep ne fonctionnera pas (sauf si vous utilisez [~ # ~] gnu [~ # ~]grep - voir ci-dessous), mais l'ajout de cut aide:

readarray -t values < <(grep '"' myfile.json | cut -d '"' -f4)

[~ # ~] gnu [~ # ~]grep: Utilisation de -P pour prendre en charge les PCRE, qui prennent en charge \K pour supprimer tout ce qui a été trouvé jusqu'à présent (une alternative plus flexible à une assertion rétrospective) ainsi que les assertions prospectives ((?=...)):

readarray -t values < <(grep -Po ':\s*"\K.+(?="\s*,?\s*$)' myfile.json)

Enfin, voici une solution pure Bash (3.x +) :

Ce qui en fait une alternative viable en termes de performances, c'est que aucun utilitaire externe n'est appelé dans chaque itération de boucle; cependant, pour les fichiers d'entrée plus volumineux, une solution basée sur des utilitaires externes sera beaucoup plus rapide.

#!/usr/bin/env bash

declare -a values # declare the array                                                                                                                                                                  

# Read each line and use regex parsing (with Bash's `=~` operator)
# to extract the value.
while read -r line; do
  # Extract the value from between the double quotes
  # and add it to the array.
  [[ $line =~ :[[:blank:]]+\"(.*)\" ]] && values+=( "${BASH_REMATCH[1]}" )
done < myfile.json                                                                                                                                          

declare -p values # print the array

[1] Voici à quoi une solution robuste basée sur jq ressemblerait (Bash 4.x):
readarray -t values < <(jq -r '.[]' myfile.json)

17
mklement0

jq est assez bon pour résoudre ce problème

paste -s <(jq '.files[].name' YourJsonString) <(jq '.files[].age' YourJsonString) <( jq '.files[].websiteurl' YourJsonString) 

Pour que vous obteniez un tableau et que vous puissiez grep toutes les lignes ou awk imprimer toutes les colonnes que vous voulez

3
Dr_Hope

Vous pouvez utiliser une doublure sed one pour y parvenir:

array=( $(sed -n "/{/,/}/{s/[^:]*:[[:blank:]]*//p;}" json ) )

Résultat:

$ echo ${array[@]}
"Amanda" "25" "http://mywebsite.com"

Si vous n'avez pas besoin/ne voulez pas de guillemets, le sed suivant les supprimera:

array=( $(sed -n '/{/,/}/{s/[^:]*:[^"]*"\([^"]*\).*/\1/p;}' json) )

Résultat:

$ echo ${array[@]}
Amanda 25 http://mywebsite.com

Cela fonctionnera également si vous avez plusieurs entrées, comme

$ cat json
{
  "name"       : "Amanda" 
  "age"        : "25"
  "websiteurl" : "http://mywebsite.com"
}

{
   "name"       : "samantha"
   "age"        : "31"
   "websiteurl" : "http://anotherwebsite.org"
}

$ echo ${array[@]}
Amanda 25 http://mywebsite.com samantha 31 http://anotherwebsite.org

MISE À JOUR:

Comme indiqué par mklement0 dans les commentaires, il peut y avoir un problème si le fichier contient des espaces incorporés, par exemple "name" : "Amanda lastname". Dans ce cas, Amanda et lastname seront tous deux lus dans des champs de tableau séparés chacun. Pour éviter cela, vous pouvez utiliser readarray, par exemple,

readarray -t array < <(sed -n '/{/,/}/{s/[^:]*:[^"]*"\([^"]*\).*/\1/p;}' json2)

Cela permettra également de résoudre tous les problèmes de globalisation, également mentionnés dans les commentaires.

1
nautical