Hvordan analysere JSON-filer på Linux-kommandolinjen med jq

JSON er et av de mest populære formatene for overføring av tekstbaserte data rundt på nettet. Det er overalt, og du kommer garantert over det. Vi viser deg hvordan du håndterer det fra Linux-kommandolinjen ved å bruke jq-kommandoen.

JSON og jq

JSON står for JavaScript-objektnotasjon. Det er et opplegg som lar data kodes inn i vanlige tekstfiler, på en selvbeskrivende måte. Det er ingen kommentarer i en JSON-fil – innholdet skal være selvforklarende. Hver dataverdi har en tekststreng kalt «navn» eller «nøkkel». Dette forteller deg hva dataverdien er. Sammen er de kjent som navn:verdi-par, eller nøkkel:verdi-par. Et kolon (:) skiller en nøkkel fra verdien.

Et «objekt» er en samling nøkkel:verdi-par. I en JSON-fil begynner et objekt med en åpen krøllete klammeparentes ({) og slutter med en avsluttende klammeparentes (}). JSON støtter også «matriser», som er ordnede lister med verdier. En matrise begynner med en åpningsparentes ([) and ends with a closing one (]).

Ut fra disse enkle definisjonene kan det selvsagt oppstå vilkårlig kompleksitet. For eksempel kan objekter nestes i objekter. Objekter kan inneholde arrays, og arrays kan også inneholde objekter. Alle kan ha åpne hekkenivåer.

I praksis, men hvis utformingen av JSON-data er kronglete, bør utformingen av dataoppsettet sannsynligvis bruke en ny vurdering. Selvfølgelig, hvis du ikke genererer JSON-dataene, bare prøver å bruke dem, har du ikke noe å si i layouten. I de tilfellene må du dessverre bare forholde deg til det.

De fleste programmeringsspråk har biblioteker eller moduler som lar dem analysere JSON-data. Dessverre, Bash-skallet har ingen slik funksjonalitet.

Nødvendighet er oppfinnelsens mor, men jq-verktøyet ble født! Med jq kan vi enkelt analysere JSON i Bash-skallet. Og det spiller ingen rolle om du må jobbe med velkonstruert, elegant JSON, eller tingene mareritt er laget av.

Hvordan installere jq

Vi måtte installere jq på alle Linux-distribusjonene vi brukte for å undersøke denne artikkelen.

For å installere jq på Ubuntu, skriv inn denne kommandoen:

sudo apt-get install jq

De

For å installere jq på Fedora, skriv inn denne kommandoen:

sudo dnf install jq

De

For å installere jq på Manjaro, skriv inn denne kommandoen:

sudo pacman -Sy jq

De

Hvordan gjøre JSON lesbar

JSON bryr seg ikke om mellomrom, og layout påvirker det ikke. Så lenge det følger reglene for JSON-grammatikk, systemer som behandler JSON kan lese og forstå det. På grunn av dette blir JSON ofte overført som en enkel, lang streng, uten hensyn til layout. Dette sparer litt plass fordi tabulatorer, mellomrom og nye linjetegn ikke trenger å være inkludert i JSON. Selvfølgelig er ulempen med alt dette når et menneske prøver å lese det.

La oss trekke et kort JSON-objekt fra NASA nettstedet som forteller oss stillingen av Internasjonal romstasjon. Vi bruker curl, som kan laste ned filer for å hente JSON-objektet for oss.

Vi bryr oss ikke om noen av statusmeldingene som curl vanligvis genererer, så vi skriver inn følgende ved å bruke alternativet -s (stille):

curl -s http://api.open-notify.org/iss-now.json

De

Nå, med litt innsats, kan du lese dette. Du må velge ut dataverdiene, men det er ikke enkelt eller praktisk. La oss gjenta dette, men denne gangen går vi gjennom jq.

jq bruker filtre for å analysere JSON, og det enkleste av disse filtrene er et punktum (.), som betyr «skriv ut hele objektet.» Som standard, jq pene utskrifter utgangen.

Vi setter alt sammen og skriver følgende:

curl -s http://api.open-notify.org/iss-now.json | jq .

De

Det er mye bedre! Nå kan vi se nøyaktig hva som skjer.

Hele objektet er pakket inn i krøllete seler. Den inneholder to nøkkel:navn-par: melding og tidsstempel. Den inneholder også et objekt kalt iss_position, som inneholder to nøkkel:verdi-par: lengdegrad og breddegrad.

Vi prøver dette en gang til. Denne gangen skriver vi følgende, og omdirigerer utdataene til en fil kalt «iss.json»:

curl -s http://api.open-notify.org/iss-now.json | jq . > iss.json
cat iss.json

De iss.json» og «cat iss.json» kommandoer i et terminalvindu.» width=”646″ height=”262″ onload=”pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);” onerror=”this.onerror=null;pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);”>

Dette gir oss en godt utformet kopi av JSON-objektet på harddisken vår.

Få tilgang til dataverdier

Som vi så ovenfor, kan jq trekke ut dataverdier som sendes gjennom fra JSON. Det kan også fungere med JSON lagret i en fil. Vi kommer til å jobbe med lokale filer slik at kommandolinjen ikke er rotete med krøllekommandoer. Dette burde gjøre det litt lettere å følge med.

Den enkleste måten å trekke ut data fra en JSON-fil på er å oppgi et nøkkelnavn for å få dataverdien. Skriv inn et punktum og nøkkelnavnet uten mellomrom. Dette lager et filter fra nøkkelnavnet. Vi må også fortelle jq hvilken JSON-fil som skal brukes.

Vi skriver inn følgende for å hente meldingsverdien:

jq .message iss.json

De

jq skriver ut teksten til meldingsverdien i terminalvinduet.

Hvis du har et nøkkelnavn som inkluderer mellomrom eller tegnsetting, må du pakke filteret inn i anførselstegn. Man er vanligvis forsiktig med å bruke tegn, tall og understreking, slik at JSON-nøkkelnavnene ikke er problematiske.

Først skriver vi følgende for å hente tidsstempelverdien:

jq .timestamp iss.json

De

Tidsstempelverdien hentes og skrives ut i terminalvinduet.

Men hvordan kan vi få tilgang til verdiene inne i iss_position-objektet? Vi kan bruke JSON-punktnotasjonen. Vi vil inkludere iss_position-objektnavnet i «banen» til nøkkelverdien. For å gjøre dette, vil navnet på objektet nøkkelen er inni gå foran navnet på selve nøkkelen.

Vi skriver inn følgende, inkludert latitude-nøkkelnavnet (merk at det ikke er mellomrom mellom «.iss_position» og «.latitude»):

jq .iss_position.latitude iss.json

De

For å trekke ut flere verdier, må du gjøre følgende:

List opp nøkkelnavnene på kommandolinjen.
Skill dem med komma (,).
Sett dem i anførselstegn (“) eller apostrof («).

Med det i tankene skriver vi følgende:

jq ".iss_position.latitude, .timestamp" iss.json

De

De to verdiene skrives ut til terminalvinduet.

Arbeid med matriser

La oss ta et annet JSON-objekt fra NASA.

Denne gangen bruker vi en liste over astronautene som er i verdensrommet akkurat nå:

curl -s http://api.open-notify.org/astros.json

De

Ok, det fungerte, så la oss gjøre det igjen.

Vi skriver følgende for å sende den gjennom jq og omdirigere den til en fil kalt «astro.json»:

curl -s http://api.open-notify.org/astros.json | jq . > astro.json

De astros.json”-kommandoen i et terminalvindu.» width=”646″ height=”77″ onload=”pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);” onerror=”this.onerror=null;pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);”>

La oss nå skrive følgende for å sjekke filen vår:

less astro.json

De

Som vist nedenfor ser vi nå listen over astronauter i verdensrommet, så vel som deres romfartøy.

Utgang fra

Dette JSON-objektet inneholder en matrise kalt folk. Vi vet at det er en matrise på grunn av åpningsbraketten ([) (highlighted in the screenshot above). It’s an array of objects that each contain two key:value pairs:  name and craft.

Like we did earlier, we can use the JSON dot notation to access the values. We must also include the brackets ([]) i navnet på matrisen.

Med alt dette i tankene skriver vi følgende:

jq ".people[].name" astro.json

De

Denne gangen skrives alle navneverdiene ut til terminalvinduet. Det vi ba jq om å gjøre var å skrive ut navneverdien for hvert objekt i matrisen. Ganske pent, ikke sant?

Vi kan hente navnet på et enkelt objekt hvis vi setter dets posisjon i matrisen i parentes ([]) på kommandolinjen. Arrayen bruker null-offset indekseringsom betyr at objektet i den første posisjonen til matrisen er null.

For å få tilgang til det siste objektet i matrisen kan du bruke -1; for å få det nest siste objektet i matrisen, kan du bruke -2, og så videre.

Noen ganger gir JSON-objektet antall elementer i arrayet, noe som er tilfellet med denne. Sammen med matrisen inneholder den et nøkkel:navn-par kalt nummer med en verdi på seks.

Følgende antall objekter er i denne matrisen:

jq ".people[1].name" astro.json
jq ".people[3].name" astro.json
jq ".people[-1].name" astro.json
jq ".people[-2].name" astro.json

De

Du kan også angi et start- og sluttobjekt i matrisen. Dette kalles «skjæring», og det kan være litt forvirrende. Husk at matrisen bruker en nullforskyvning.

For å hente objektene fra indeksposisjon to, opp til (men ikke inkludert) objektet i indeksposisjon fire, skriver vi følgende kommando:

jq ".people[2:4]" astro.json

De

Dette skriver ut objektene ved matriseindeks to (det tredje objektet i matrisen) og tre (det fjerde objektet i matrisen). Den stopper behandlingen ved matriseindeks fire, som er det femte objektet i matrisen.

Måten å forstå dette bedre på er å eksperimentere på kommandolinjen. Du vil snart se hvordan det fungerer.

Hvordan bruke rør med filtre

Du kan overføre utdata fra ett filter til et annet, og du trenger ikke å lære et nytt symbol. Det samme som Linux-kommandolinjen, jq bruker den vertikale linjen (|) for å representere et rør.

Vi ber jq om å føre folk-arrayen inn i .name-filteret, som skal liste navnene på astronautene i terminalvinduet.

Vi skriver følgende:

jq ".people[] | .name" astro.json

De

Opprette matriser og endre resultater

Vi kan bruke jq til å lage nye objekter, for eksempel arrays. I dette eksemplet trekker vi ut tre verdier og lager en ny matrise som inneholder disse verdiene. Legg merke til åpningen ([) and closing brackets (]) er også de første og siste tegnene i filterstrengen.

Vi skriver følgende:

jq "[.iss-position.latitude, iss_position.longitude, .timestamp]" iss.json

De

Utdataene er pakket inn i parentes og atskilt med kommaer, noe som gjør det til en riktig utformet matrise.

Numeriske verdier kan også manipuleres etter hvert som de hentes. La oss trekke tidsstemplet fra ISS-posisjonsfilen, og deretter trekke den ut igjen og endre verdien som returneres.

For å gjøre det skriver vi følgende:

jq ".timestamp" iss.json
jq ".timestamp - 1570000000" iss.json

De

Dette er nyttig hvis du trenger å legge til eller fjerne en standard offset fra en rekke verdier.

La oss skrive følgende for å minne oss selv på hva iss.json-filen inneholder:

jq . iss.json

De

La oss si at vi ønsker å bli kvitt meldingsnøkkel:verdi-paret. Det har ikke noe å gjøre med posisjonen til den internasjonale romstasjonen. Det er bare et flagg som indikerer at plasseringen ble hentet. Hvis det er overskudd til kravene, kan vi avstå fra det. (Du kan også bare ignorere det.)

Vi kan bruke jqs slettefunksjon, del(), for å slette et nøkkel:verdi-par. For å slette meldingsnøkkel:verdi-paret, skriver vi denne kommandoen:

jq "del(.message)" iss.json

De

Merk at dette faktisk ikke sletter den fra «iss.json»-filen; det fjerner det bare fra utdataene til kommandoen. Hvis du trenger å opprette en ny fil uten meldingsnøkkel:verdi-paret i den, kjør kommandoen og omdiriger deretter utdataene til en ny fil.

Mer kompliserte JSON-objekter

La oss hente noen flere NASA-data. Denne gangen bruker vi et JSON-objekt som inneholder informasjon om meteornedslagssteder fra rundt omkring i verden. Dette er en større fil med en langt mer komplisert JSON-struktur enn de vi har behandlet tidligere.

Først skriver vi følgende for å omdirigere den til en fil kalt «strikes.json»:

curl -s https://data.nasa.gov/resource/y77d-th95.json | jq . > strikes.json

De strikes.json»-kommandoen i et terminalvindu.» width=”646″ height=”77″ onload=”pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);” onerror=”this.onerror=null;pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);”>

For å se hvordan JSON ser ut, skriver vi følgende:

less strikes.json

De

Som vist nedenfor, begynner filen med en åpningsparentes ([), so the entire object is an array. The objects in the array are collections of key:value pairs, and there’s a nested object called geolocation. The geolocation object contains further key:value pairs, and an array called coordinates.

Output from the

Let’s retrieve the names of the meteor strikes from the object at index position 995 through the end of the array.

We’ll type the following to pipe the JSON through three filters:

jq ".[995:] |  .[] |  .name" strikes.json

De

Filtrene fungerer på følgende måter:

.[995:]: Dette forteller jq å behandle objektene fra matriseindeks 995 til slutten av matrisen. Ingen tall etter kolon ( : ) er det som forteller jq å fortsette til slutten av matrisen.
.[]: Denne array-iteratoren forteller jq å behandle hvert objekt i arrayet.
.name: Dette filteret trekker ut navneverdien.

Med en liten endring kan vi trekke ut de siste 10 objektene fra matrisen. En «-10» instruerer jq om å begynne å behandle objekter 10 tilbake fra slutten av matrisen.

Vi skriver følgende:

jq ".[-10:] | .[] | .name" strikes.json

De

Akkurat som vi har gjort i tidligere eksempler, kan vi skrive følgende for å velge et enkelt objekt:

jq ".[650].name" strikes.json

De

Vi kan også bruke skjæring på strenger. For å gjøre det, skriver vi følgende for å be om de fire første tegnene i navnet på objektet i array index 234:

jq ".[234].name[0:4]" strikes.json

De

Vi kan også se et spesifikt objekt i sin helhet. For å gjøre dette skriver vi følgende og inkluderer en matriseindeks uten noen nøkkel:verdi-filtre:

jq ".[234]" strikes.json

De

Hvis du bare vil se verdiene, kan du gjøre det samme uten nøkkelnavnene.

For vårt eksempel skriver vi denne kommandoen:

jq ".[234][]" strikes.json

De

For å hente flere verdier fra hvert objekt, skiller vi dem med kommaer i følgende kommando:

jq ".[450:455] | .[] | .name, .mass" strikes.json

De

Hvis du vil hente nestede verdier, må du identifisere objektene som danner «banen» til dem.

For å referere til koordinatverdiene, må vi for eksempel inkludere den altomfattende matrisen, det nestede geolokaliseringsobjektet og den nestede koordinatmatrisen, som vist nedenfor.

For å se koordinatverdiene for objektet ved indeksposisjon 121 i matrisen, skriver vi følgende kommando:

jq ".[121].geolocation.coordinates[]" strikes.json

De

Lengdefunksjonen

Jq-lengdefunksjonen gir forskjellige beregninger i henhold til hva den har blitt brukt, for eksempel:

Strenger: Lengden på strengen i byte.
Objekter: Antall nøkkel:verdi-par i objektet.
Matriser: Antall matriseelementer i matrisen.

Følgende kommando returnerer lengden på navneverdien i 10 av objektene i JSON-matrisen, fra indeksposisjon 100:

jq ".[100:110] | .[].name | length" strikes.json

De

For å se hvor mange nøkkel:verdi-par som er i det første objektet i matrisen, skriver vi denne kommandoen:

jq ".[0] | length" strikes.json

De

Tastene Funksjon

Du kan bruke tastefunksjonen for å finne ut om JSON-en du må jobbe med. Den kan fortelle deg hva navnene på nøklene er, og hvor mange objekter det er i en matrise.

For å finne nøklene i personobjektet i «astro.json»-filen, skriver vi denne kommandoen:

jq ".people.[0] | keys" astro.json

De

For å se hvor mange elementer som er i personarrayet, skriver vi denne kommandoen:

jq ".people | keys" astro.json

De

Dette viser at det er seks matriseelementer med null forskyvning, nummerert fra null til fem.

funksjonen has().

Du kan bruke has()-funksjonen for å spørre JSON og se om et objekt har et bestemt nøkkelnavn. Merk at nøkkelnavnet må settes inn i anførselstegn. Vi pakker filterkommandoen inn i enkle anførselstegn («), som følger:

jq '.[] | has("nametype")' strikes.json

Hvert objekt i arrayet er sjekket, som vist nedenfor.

Hvis du vil sjekke et spesifikt objekt, inkluderer du dets indeksposisjon i array-filteret, som følger:

jq '.[678] | has("nametype")' strikes.json

Ikke gå i nærheten av JSON uten den

Jq-verktøyet er det perfekte eksempelet på den profesjonelle, kraftige, raske programvaren som gjør det til en fornøyelse å leve i Linux-verdenen.

Dette var bare en kort introduksjon til de vanlige funksjonene til denne kommandoen – det er mye mer. Sørg for å sjekke ut den omfattende jq manual hvis du vil grave dypere.