2016-07-14 47 views
8

Sto cercando di analizzare un oggetto JSON all'interno di uno script di shell in un array.Analizza in array in uno script di shell

ad esempio: [Amanda, 25, http://mywebsite.com]

Il JSON assomiglia:

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

Non voglio usare tutte le librerie, che sarebbe stato meglio se avessi potuto usare un'espressione regolare o grep . Ho fatto:

myfile.json | grep name 

Questo mi dà "nome": "Amanda". Potrei farlo in un ciclo per ogni riga del file e aggiungerlo a un array, ma ho solo bisogno del lato destro e non dell'intera linea.

+3

Usa 'jq' per questo. – sjsam

+0

Dai un'occhiata a [\ [this \]] (http://unix.stackexchange.com/questions/177843/parse-one-field-from-an-json-array-into-bash-array) domanda e mostra un po 'di sforzo da parte tua per risolvere questo. – sjsam

+1

Questo 'cat myfile.json | nome grep | cut -d ':' -f2' potrebbe aiutare. –

risposta

14

Se proprio non è possibile utilizzare una corretta parser JSON come jq[1] , provare un awk soluzione basata su:

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) 

Questo negozi tutte le proprietà valori nell'array Bash ${values[@]}, che è possibile controllare con
declare -p values.

Queste soluzioni hanno limitazioni:

  • ogni proprietà deve essere su una riga separata,
  • tutti i valori devono essere a doppio citato,
  • incorporati virgolette doppie escape non sono supportati.

Tutte queste limitazioni rafforzano la raccomandazione di utilizzare un parser JSON corretto.


Nota: Le seguenti soluzioni alternative usare il 4.x + readarray -t values comando bash, ma funziona anche con l'alternativa Bash 3.x, IFS=$'\n' read -d '' -ra values.

grep + cut combinazione: un unico comando grep non farà (a meno che non si utilizza GNU grep - vedi sotto), ma aggiungendo cut aiuta:

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

GNUgrep: Utilizzo di -P per supportare PCREs, quale supporto t \K di mollare tutto abbinato finora (un più flessibile alternativa ad un look-dietro affermazione), così come asserzioni look-ahead ((?=...)):

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

Infine, ecco un puro Bash (3 .x +) soluzione:

ciò che rende questo una valida alternativa in termini di prestazioni è che non utility esterne vengono chiamati in ogni iterazione del ciclo; tuttavia, per i file di input più grandi, una soluzione basata su utility esterne sarà molto più veloce.

#!/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] Ecco cosa una robusta soluzione basata su jq sarebbe simile (Bash 4.x):
readarray -t values < <(jq -r '.[]' myfile.json)

0

è possibile utilizzare un sed uno di linea per raggiungere questo:

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

Risultato:

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

Se non avete bisogno/vogliono le virgolette allora la seguente sed faranno finita con loro:

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

Risultato:

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

Si lavorerà anche se hai più voci, ad esempio

$ 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 

UPDATE:

Come indicato da mklement0 nei commenti, potrebbe esserci un problema se il file contiene spazi bianchi incorporati, ad esempio "name" : "Amanda lastname". In questo caso, Amanda e lastname vengono entrambi letti in campi di array separati ciascuno. Per evitare questo è possibile utilizzare readarray, per esempio,

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

Questo sarà anche prendersi cura di eventuali problemi globbing, citato anche nei commenti.

+3

Si prega di non analizzare l'output del comando in un array con 'array = ($ (...))' (anche se capita di lavorare con l'input di esempio): non funziona come previsto con spazi bianchi incorporati e può risultare in accidentale globbing. – mklement0

+0

@ mklement0 Puoi dare un esempio di come dovrebbe apparire il contenuto del file di esempio per l'occorrenza del globbing accidentale? – nautical

+0

Per vedere quale approccio fai agli spazi bianchi incorporati, esamina la matrice risultante da 'array = ($ (echo 'a b'))'; per vedere gli effetti del globbing accidentale, prova 'array = ($ (echo 'a * is born'))'. – mklement0

3

JQ è abbastanza buono per risolvere questo problema

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

In modo da ottenere un tavolo e si può grep eventuali righe o stampa awk eventuali colonne che si desidera

Problemi correlati