2016-07-14 13 views
8

Próbuję parsować obiekt JSON w skrypcie powłoki do tablicy.Parsowanie JSON do tablicy w skrypcie powłoki

np: [Amanda, 25, http://mywebsite.com]

JSON wygląda następująco:

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

Nie chcę używać żadnych bibliotek, byłoby najlepiej, jeśli mogę użyć wyrażenia regularnego lub grep . Zrobiłem:

myfile.json | grep name 

To daje mi "imię": "Amanda". Mogłem to zrobić w pętli dla każdej linii w pliku i dodać ją do tablicy, ale potrzebuję tylko prawej strony, a nie całej linii.

+3

Wykorzystanie 'jq' do tego. – sjsam

+0

Zajrzyj do pytania [\ [to \]] (http://unix.stackexchange.com/questions/177843/parse-one-field-from-an-json-array-into-bash-array) nam trochę wysiłku z twojej strony, aby rozwiązać ten problem. – sjsam

+1

Ten 'cat myfile.json | grep nazwa | cut -d ':' -f2' może pomóc. –

Odpowiedz

14

Jeśli naprawdę nie można użyć właściwego parsera JSON takie jak jq[1] , spróbuj awk opartych rozwiązanie:

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) 

w tym sklepie wszystkie właściwości wartości w tablicy Bash ${values[@]}, które można sprawdzić za pomocą
declare -p values.

Te rozwiązania mają ograniczenia:

  • każda nieruchomość musi być w oddzielnym wierszu,
  • wszystkie wartości muszą być podwójnie cytowany
  • osadzonych uciekły cudzysłowy nie są obsługiwane.

Wszystkie te ograniczenia potwierdzają zalecenie używania odpowiedniego parsera JSON.


Uwaga: Poniższe rozwiązania alternatywne użyć 4.x + readarray -t values polecenia bash, ale również pracować z alternatywą Bash 3.x IFS=$'\n' read -d '' -ra values.

grep + cut kombinacja: pojedyncza komenda grep nie zrobi (chyba, że ​​używasz GNUgrep - patrz poniżej), ale dodanie cut pomaga:

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

GNUgrep: Używanie -P do obsługi PCRE, które wspiera t \K rzucić wszystko dopasowane do tej pory (a bardziej elastyczną alternatywę dla twierdzenia przeglądowej tyłu), jak również twierdzenia antycypowana ((?=...)):

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

Wreszcie, oto czysty atakujących (3 .x +), roztwór:

co czyni przydatnym ten sposób alternatywę względem wyników jest to, że żadne z zewnętrznych narzędzi nazywane są w każdej iteracji; jednak w przypadku większych plików wejściowych rozwiązanie oparte na zewnętrznych narzędziach będzie znacznie szybsze.

#!/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] Oto co solidna jq -na rozwiązanie będzie wyglądać (Bash 4.x):
readarray -t values < <(jq -r '.[]' myfile.json)

0

można użyć sed jedną wkładkę Aby to osiągnąć:

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

Wynik:

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

Jeśli nie potrzebujesz/chcesz cudzysłów następnie następujące sed będzie pozbycie się z nich:

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

Wynik:

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

To będzie również pracować jeśli masz wiele pozycji, takich jak

$ 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 

AKTUALIZACJA:

Jak wskazano przez mklement0 w komentarzach, może wystąpić problem, jeśli plik zawiera osadzone białe znaki, np. "name" : "Amanda lastname". W tym przypadku oba Amanda i lastname będą odczytywane do oddzielnych pól macierzowych. W tym celu można użyć readarray, np uniknąć,

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

Będzie to również zadbać o wszelkich masek kwestii, również wspomniane w komentarzach.

+3

Nie parsuj danych wyjściowych polecenia do tablicy z 'tablica = ($ (...))' (nawet jeśli działa z przykładowym wejściem): nie działa zgodnie z przeznaczeniem z osadzonymi białymi znakami i może powodować w przypadkowym globbingowaniu. – mklement0

+0

@ mklement0 Czy możesz podać przykład, w jaki sposób zawartość przykładowego pliku musiałaby wyglądać tak, aby przypadkowo wystąpił globbing? – nautical

+0

Aby zobaczyć, co twoje podejście robi z osadzonymi białymi znakami, sprawdź tablicę, która wynika z 'array = ($ (echo 'a b'))'; aby zobaczyć efekty przypadkowego globowania, wypróbuj 'tablica = ($ (echo 'a * się rodzi'))'. – mklement0

3

jq jest wystarczająco dobry, aby rozwiązać ten problem

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

Tak, że masz stolik i można grep żadnych wierszy lub drukuj awk żadnych kolumn chcesz