Hur man analyserar JSON-filer på Linux-kommandoraden med jq

JSON är ett av de mest populära formaten för att överföra textbaserad data på webben. Det finns överallt, och du kommer säkert att stöta på det. Vi visar dig hur du hanterar det från Linux-kommandoraden med kommandot jq.

JSON och jq

JSON står för JavaScript-objektnotation. Det är ett schema som gör att data kan kodas till vanliga textfiler, på ett självbeskrivande sätt. Det finns inga kommentarer i en JSON-fil – innehållet bör vara självförklarande. Varje datavärde har en textsträng som kallas ”namn” eller ”nyckel”. Detta talar om vad datavärdet är. Tillsammans kallas de namn:värde-par eller nyckel:värde-par. Ett kolon (:) skiljer en nyckel från dess värde.

Ett ”objekt” är en samling nyckel:värdepar. I en JSON-fil börjar ett objekt med ett öppet lockigt klammerparentes ({) och slutar med ett avslutande klammerparentes (}). JSON stöder också ”arrayer”, som är ordnade värdelistor. En array börjar med en öppningsparentes ([) and ends with a closing one (]).

Ur dessa enkla definitioner kan naturligtvis godtycklig komplexitet uppstå. Till exempel kan objekt kapslas inuti objekt. Objekt kan innehålla arrayer och arrayer kan också innehålla objekt. Alla som kan ha öppna nivåer av häckning.

Men i praktiken, om layouten för JSON-data är invecklad, bör utformningen av datalayouten förmodligen tänka om. Naturligtvis, om du inte genererar JSON-data, bara försöker använda den, har du inget att säga till om i dess layout. I de fallen är det tyvärr bara att ta itu med det.

De flesta programmeringsspråk har bibliotek eller moduler som tillåter dem att analysera JSON-data. Tyvärr, Bash-skalet har ingen sådan funktion.

Nödvändigheten är uppfinningens moder, men jq-verktyget föddes! Med jq kan vi tolka enkelt JSON i Bash-skalet. Och det spelar ingen roll om du måste arbeta med välkonstruerad, elegant JSON, eller de saker som mardrömmar är gjorda av.

Hur man installerar jq

Vi var tvungna att installera jq på alla Linux-distributioner som vi använde för att undersöka den här artikeln.

För att installera jq på Ubuntu skriv detta kommando:

sudo apt-get install jq

De

För att installera jq på Fedora, skriv detta kommando:

sudo dnf install jq

De

För att installera jq på Manjaro, skriv detta kommando:

sudo pacman -Sy jq

De

Hur man gör JSON läsbar

JSON bryr sig inte om vitt utrymme, och layouten påverkar det inte. Så länge det följer reglerna för JSON grammatik, system som bearbetar JSON kan läsa och förstå det. På grund av detta sänds JSON ofta som en enkel, lång sträng, utan hänsyn till layout. Detta sparar lite utrymme eftersom tabbar, blanksteg och nya radstecken inte behöver inkluderas i JSON. Naturligtvis är nackdelen med allt detta när en människa försöker läsa det.

Låt oss dra ett kort JSON-objekt från NASA webbplats som berättar om positionen av Internationell rymdstation. Vi använder curl, som kan ladda ner filer för att hämta JSON-objektet åt oss.

Vi bryr oss inte om några av de statusmeddelanden som curl vanligtvis genererar, så vi skriver följande med alternativet -s (tyst):

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

De

Nu, med lite ansträngning, kan du läsa detta. Du måste välja ut datavärdena, men det är inte lätt eller bekvämt. Låt oss upprepa detta, men den här gången tar vi igenom jq.

jq använder filter för att analysera JSON, och det enklaste av dessa filter är en punkt (.), vilket betyder ”skriv ut hela objektet.” Som standard, jq snygga tryck utgången.

Vi lägger ihop allt och skriver följande:

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

De

Det är mycket bättre! Nu kan vi se exakt vad som händer.

Hela föremålet är inlindat i lockiga hängslen. Den innehåller två nyckel:namn-par: meddelande och tidsstämpel. Den innehåller också ett objekt som heter iss_position, som innehåller två nyckel:värdepar: longitud och latitud.

Vi ska prova detta en gång till. Den här gången skriver vi följande och omdirigerar utdata till en fil som heter ”iss.json”:

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

De iss.json” och ”cat iss.json” kommandon i ett terminalfönster.’ width=”646″ height=”262″ onload=”pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);” onerror=”this.onerror=null;pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);”>

Detta ger oss en välplanerad kopia av JSON-objektet på vår hårddisk.

Få åtkomst till datavärden

Som vi såg ovan kan jq extrahera datavärden som leds igenom från JSON. Det kan också fungera med JSON lagrad i en fil. Vi kommer att arbeta med lokala filer så att kommandoraden inte är belamrad med curl-kommandon. Detta borde göra det lite lättare att följa.

Det enklaste sättet att extrahera data från en JSON-fil är att ange ett nyckelnamn för att få dess datavärde. Skriv en punkt och nyckelnamnet utan mellanslag mellan dem. Detta skapar ett filter från nyckelnamnet. Vi måste också tala om för jq vilken JSON-fil som ska användas.

Vi skriver följande för att hämta meddelandevärdet:

jq .message iss.json

De

jq skriver ut texten i meddelandevärdet i terminalfönstret.

Om du har ett nyckelnamn som innehåller mellanslag eller skiljetecken, måste du linda dess filter med citattecken. Man brukar vara försiktig med att endast använda tecken, siffror och understreck så att JSON-nyckelnamnen inte är problematiska.

Först skriver vi följande för att hämta tidsstämpelvärdet:

jq .timestamp iss.json

De

Tidsstämpelvärdet hämtas och skrivs ut i terminalfönstret.

Men hur kan vi komma åt värdena inuti objektet iss_position? Vi kan använda JSON-punktnotationen. Vi kommer att inkludera iss_position-objektets namn i ”sökvägen” till nyckelvärdet. För att göra detta kommer namnet på objektet nyckeln är inuti att föregå namnet på själva nyckeln.

Vi skriver följande, inklusive latitude-nyckelns namn (observera att det inte finns några mellanslag mellan ”.iss_position” och ”.latitude”):

jq .iss_position.latitude iss.json

De

För att extrahera flera värden måste du göra följande:

Lista nyckelnamnen på kommandoraden.
Separera dem med kommatecken (,).
Omge dem inom citattecken (“) eller apostrof (’).

Med det i åtanke skriver vi följande:

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

De

De två värdena skrivs ut i terminalfönstret.

Arbeta med Arrays

Låt oss ta ett annat JSON-objekt från NASA.

Den här gången använder vi en lista över de astronauter som befinner sig i rymden just nu:

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

De

Okej, det fungerade, så låt oss göra det igen.

Vi skriver följande för att leda den genom jq och omdirigera den till en fil som heter ”astro.json”:

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

De astros.json” kommandot i ett terminalfönster.’ width=”646″ height=”77″ onload=”pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);” onerror=”this.onerror=null;pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);”>

Låt oss nu skriva följande för att kontrollera vår fil:

less astro.json

De

Som visas nedan ser vi nu listan över astronauter i rymden, såväl som deras rymdfarkoster.

Utgång från

Detta JSON-objekt innehåller en array som kallas människor. Vi vet att det är en array på grund av öppningsparentesen ([) (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 arrayens namn.

Med allt detta i åtanke skriver vi följande:

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

De

Den här gången skrivs alla namnvärden ut i terminalfönstret. Det vi bad jq att göra var att skriva ut namnvärdet för varje objekt i arrayen. Ganska snyggt va?

Vi kan hämta namnet på ett enskilt objekt om vi sätter dess position i arrayen inom parentes ([]) på kommandoraden. Arrayen använder nollförskjutningsindexeringvilket betyder att objektet i den första positionen i arrayen är noll.

För att komma åt det sista objektet i arrayen kan du använda -1; för att få det näst sista objektet i arrayen kan du använda -2 och så vidare.

Ibland tillhandahåller JSON-objektet antalet element i arrayen, vilket är fallet med detta. Tillsammans med arrayen innehåller den ett nyckel:namn-par som kallas nummer med värdet sex.

Följande antal objekt finns i denna array:

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 också tillhandahålla ett start- och slutobjekt i arrayen. Detta kallas ”skiva” och det kan vara lite förvirrande. Kom ihåg att matrisen använder en nollförskjutning.

För att hämta objekten från indexposition två, upp till (men inte inklusive) objektet vid indexposition fyra, skriver vi följande kommando:

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

De

Detta skriver ut objekten vid arrayindex två (det tredje objektet i arrayen) och tre (det fjärde objektet i arrayen). Den slutar bearbeta vid arrayindex fyra, som är det femte objektet i arrayen.

Sättet att bättre förstå detta är att experimentera på kommandoraden. Du får snart se hur det fungerar.

Hur man använder rör med filter

Du kan röra utdata från ett filter till ett annat, och du behöver inte lära dig en ny symbol. På samma sätt som Linux-kommandoraden använder jq den vertikala stapeln (|) för att representera ett rör.

Vi säger åt jq att leda folkarrayen in i .name-filtret, som bör lista namnen på astronauterna i terminalfönstret.

Vi skriver följande:

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

De

Skapa matriser och ändra resultat

Vi kan använda jq för att skapa nya objekt, till exempel arrayer. I det här exemplet extraherar vi tre värden och skapar en ny array som innehåller dessa värden. Notera öppningen ([) and closing brackets (]) är också de första och sista tecknen i filtersträngen.

Vi skriver följande:

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

De

Utdata är inslagna inom parentes och separerade med kommatecken, vilket gör det till en korrekt utformad array.

Numeriska värden kan också manipuleras när de hämtas. Låt oss dra tidsstämpeln från ISS-positionsfilen och sedan extrahera den igen och ändra värdet som returneras.

För att göra det skriver vi följande:

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

De

Detta är användbart om du behöver lägga till eller ta bort en standardoffset från en uppsättning värden.

Låt oss skriva följande för att påminna oss själva om vad filen iss.json innehåller:

jq . iss.json

De

Låt oss säga att vi vill bli av med meddelandenyckel:värdeparet. Det har ingenting att göra med den internationella rymdstationens position. Det är bara en flagga som indikerar att platsen har hämtats framgångsrikt. Om det är överskott till kraven kan vi avstå från det. (Du kan också bara ignorera det.)

Vi kan använda jqs raderingsfunktion, del(), för att ta bort ett nyckel:värdepar. För att ta bort meddelandenyckel:värdeparet skriver vi in ​​det här kommandot:

jq "del(.message)" iss.json

De

Observera att detta faktiskt inte tar bort det från filen ”iss.json”. det tar bara bort det från kommandots utdata. Om du behöver skapa en ny fil utan meddelandenyckel:värde-paret, kör kommandot och omdirigera sedan utdata till en ny fil.

Mer komplicerade JSON-objekt

Låt oss hämta lite mer NASA-data. Den här gången kommer vi att använda ett JSON-objekt som innehåller information om meteornedslagsplatser från hela världen. Det här är en större fil med en mycket mer komplicerad JSON-struktur än de vi har behandlat tidigare.

Först skriver vi följande för att omdirigera den till en fil som heter ”strikes.json”:

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

De strikes.json” kommandot i ett terminalfönster.’ width=”646″ height=”77″ onload=”pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);” onerror=”this.onerror=null;pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);”>

För att se hur JSON ser ut skriver vi följande:

less strikes.json

De

Som visas nedan börjar filen med en öppningsparentes ([), 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

Filtren fungerar på följande sätt:

.[995:]: Detta talar om för jq att bearbeta objekten från arrayindex 995 till slutet av arrayen. Inget nummer efter kolon ( : ) är det som talar om för jq att fortsätta till slutet av arrayen.
.[]: Denna array-iterator säger åt jq att bearbeta varje objekt i arrayen.
.name: Detta filter extraherar namnvärdet.

Med en liten förändring kan vi extrahera de sista 10 objekten från arrayen. En ”-10” instruerar jq att börja bearbeta objekt 10 tillbaka från slutet av arrayen.

Vi skriver följande:

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

De

Precis som vi har gjort i tidigare exempel kan vi skriva följande för att välja ett enskilt objekt:

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

De

Vi kan även applicera skivning på strängar. För att göra det skriver vi följande för att begära de första fyra tecknen i namnet på objektet vid arrayindex 234:

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

De

Vi kan också se ett specifikt objekt i sin helhet. För att göra detta skriver vi följande och inkluderar ett arrayindex utan några key:value-filter:

jq ".[234]" strikes.json

De

Om du bara vill se värdena kan du göra samma sak utan nyckelnamnen.

För vårt exempel skriver vi det här kommandot:

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

De

För att hämta flera värden från varje objekt separerar vi dem med kommatecken i följande kommando:

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

De

Om du vill hämta kapslade värden måste du identifiera de objekt som utgör ”sökvägen” till dem.

Till exempel, för att referera till koordinatvärdena, måste vi inkludera den allomfattande arrayen, det geolokaliseringskapslade objektet och den kapslade koordinatmatrisen, som visas nedan.

För att se koordinatvärdena för objektet vid indexposition 121 i arrayen, skriver vi följande kommando:

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

De

Längdfunktionen

Jq-längdfunktionen ger olika mätvärden beroende på vad den har tillämpats, till exempel:

Strängar: Strängens längd i byte.
Objekt: Antalet nyckel:värde-par i objektet.
Arrayer: Antalet arrayelement i arrayen.

Följande kommando returnerar längden på namnvärdet i 10 av objekten i JSON-matrisen, med start vid indexposition 100:

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

De

För att se hur många key:value-par som finns i det första objektet i arrayen, skriver vi det här kommandot:

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

De

Knapparna Funktion

Du kan använda tangentfunktionen för att ta reda på den JSON du måste arbeta med. Den kan berätta vad nycklarna heter och hur många objekt det finns i en array.

För att hitta nycklarna i personobjektet i filen ”astro.json”, skriver vi det här kommandot:

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

De

För att se hur många element som finns i folkarrayen skriver vi det här kommandot:

jq ".people | keys" astro.json

De

Detta visar att det finns sex arrayelement med nollförskjutning, numrerade från noll till fem.

Funktionen has()

Du kan använda has()-funktionen för att fråga JSON och se om ett objekt har ett speciellt nyckelnamn. Observera att nyckelnamnet måste vara omgivet av citattecken. Vi lindar filterkommandot med enkla citattecken (’), enligt följande:

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

Varje objekt i arrayen kontrolleras, som visas nedan.

Om du vill kontrollera ett specifikt objekt inkluderar du dess indexposition i arrayfiltret, enligt följande:

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

Gå inte nära JSON utan det

Verktyget jq är det perfekta exemplet på den professionella, kraftfulla, snabba programvaran som gör det till ett nöje att leva i Linux-världen.

Detta var bara en kort introduktion till de vanliga funktionerna för det här kommandot – det finns mycket mer i det. Se till att kolla in den omfattande jq manual om du vill gräva djupare.