JSON ontleden met Unix-tools

Ik probeer JSON te parseren die is geretourneerd vanuit een curl-verzoek, zoals:

curl 'http://twitter.com/users/username.json' |
    sed -e 's/[{}]/''/g' | 
    awk -v k="text" '{n=split($0,a,","); for (i=1; i<=n; i++) print a[i]}'

Het bovenstaande splitst de JSON op in velden, bijvoorbeeld:

% ...
"geo_enabled":false
"friends_count":245
"profile_text_color":"000000"
"status":"in_reply_to_screen_name":null
"source":"web"
"truncated":false
"text":"My status"
"favorited":false
% ...

Hoe druk ik een specifiek veld af (aangeduid met de -v k=text)?


Antwoord 1, autoriteit 100%

Er zijn een aantal tools die speciaal zijn ontworpen om JSON vanaf de opdrachtregel te manipuleren, en deze zullen een stuk eenvoudiger en betrouwbaarder zijn dan met Awk, zoals jq:

curl -s 'https://api.github.com/users/lambda' | jq -r '.name'

Je kunt dit ook doen met tools die waarschijnlijk al op je systeem zijn geïnstalleerd, zoals Python met behulp van de jsonmodule, en vermijd dus extra afhankelijkheden, terwijl je toch het voordeel hebt van een goede JSON-parser. Het volgende gaat ervan uit dat u UTF-8 wilt gebruiken, waarin de originele JSON zou moeten worden gecodeerd en wat de meeste moderne terminals ook gebruiken:

Python 3:

curl -s 'https://api.github.com/users/lambda' | \
    python3 -c "import sys, json; print(json.load(sys.stdin)['name'])"

Python 2:

export PYTHONIOENCODING=utf8
curl -s 'https://api.github.com/users/lambda' | \
    python2 -c "import sys, json; print json.load(sys.stdin)['name']"

Veelgestelde vragen

Waarom geen pure shell-oplossing?

De standaard POSIX/Single Unix Specification-shellis een zeer beperkte taal die geen faciliteiten bevat voor het weergeven van reeksen (lijst of arrays) of associatieve arrays (ook bekend als hashtabellen, kaarten, dicts of objecten in sommige andere talen). Dit maakt het weergeven van het resultaat van het ontleden van JSON enigszins lastig in draagbare shell-scripts. Er zijn enigszins hacky manieren om het te doen, maar veel ervan kunnen breken als sleutels of waarden bevatten bepaalde speciale tekens.

Bash 4 en hoger, zsh en ksh hebben ondersteuning voor arrays en associatieve arrays, maar deze shells zijn niet universeel beschikbaar (macOS stopte met het updaten van Bash bij Bash 3 vanwege een verandering van GPLv2 naar GPLv3, terwijl veel Linux-systemen dat niet doen heb zsh niet standaard geïnstalleerd). Het is mogelijk dat je een script zou kunnen schrijven dat zou werken in Bash 4 of zsh, waarvan er tegenwoordig een beschikbaar is op de meeste macOS-, Linux- en BSD-systemen, maar het zou moeilijk zijn om een shebang-regel te schrijven die werkte voor zo’n polyglot-script.

Ten slotte zou het schrijven van een volwaardige JSON-parser in shell een voldoende grote afhankelijkheid zijn om in plaats daarvan een bestaande afhankelijkheid zoals jq of Python te gebruiken. Het zal geen one-liner zijn, of zelfs een klein fragment van vijf regels, om een goede implementatie te doen.

Waarom gebruik je geen awk, sed of grep?

Het is mogelijk om deze tools te gebruiken om een snelle extractie uit JSON uit te voeren met een bekende vorm en op een bekende manier opgemaakt, zoals één sleutel per regel. Er zijn verschillende voorbeelden van suggesties hiervoor in andere antwoorden.

Deze tools zijn echter ontworpen voor op regels of records gebaseerde formaten; ze zijn niet ontworpen voor recursieve ontleding van overeenkomende scheidingstekens met mogelijke escape-tekens.

Dus deze snelle en vuile oplossingen die awk/sed/grep gebruiken, zijn waarschijnlijk kwetsbaar en breken als een bepaald aspect van het invoerformaat verandert, zoals het samenvouwen van witruimte of het toevoegen van extra niveaus van nesting aan de JSON-objecten, of een escaped aanhalingsteken binnen een string. Een oplossing die robuust genoeg is om alle JSON-invoer te verwerken zonder te breken, zal ook vrij groot en complex zijn, en dus niet veel anders dan het toevoegen van nog een afhankelijkheid van jqof Python.

Ik heb eerder te maken gehad met het verwijderen van grote hoeveelheden klantgegevens als gevolg van slechte invoerparsing in een shellscript, dus ik raad nooit snelle en vuile methoden aan die op deze manier kwetsbaar kunnen zijn. Als je een eenmalige verwerking uitvoert, bekijk dan de andere antwoorden voor suggesties, maar ik raad je toch ten zeerste aan om gewoon een bestaande geteste JSON-parser te gebruiken.

Historische notities

Dit antwoord raadde oorspronkelijk jsawkaan, wat nog steeds zou moeten werken, maar een beetje omslachtiger is om te gebruiken dan jq, en hangt af van de installatie van een zelfstandige JavaScript-interpreter die minder vaak voorkomt dan een Python-interpreter, dus de bovenstaande antwoorden hebben waarschijnlijk de voorkeur:

curl -s 'https://api.github.com/users/lambda' | jsawk -a 'return this.name'

Dit antwoord gebruikte oorspronkelijk ook de Twitter API van de vraag, maar die API werkt niet meer, waardoor het moeilijk is om de voorbeelden te kopiëren om te testen, en de nieuwe Twitter API vereist API-sleutels, dus ik ben overgestapt op het gebruik van de GitHub API die eenvoudig kan worden gebruikt zonder API-sleutels. Het eerste antwoord op de oorspronkelijke vraag zou zijn:

curl 'http://twitter.com/users/username.json' | jq -r '.text'

Antwoord 2, autoriteit 22%

Om snel de waarden voor een bepaalde sleutel te extraheren, gebruik ik persoonlijk graag “grep -o”, dat alleen de overeenkomst van de regex retourneert. Om bijvoorbeeld het veld “tekst” uit tweets te halen, zoiets als:

grep -Po '"text":.*?[^\\]",' tweets.json

Deze regex is robuuster dan je zou denken; het gaat bijvoorbeeld prima om met tekenreeksen met ingesloten komma’s en aanhalingstekens met escapetekens erin. Ik denk dat je met wat meer werk er een kunt maken die gegarandeerd de waarde eruit haalt, als het atomair is. (Als het nesting heeft, kan een regex het natuurlijk niet doen.)

En om verder op te schonen (hoewel de originele ontsnapping van de string behouden blijft), kun je zoiets gebruiken als: | perl -pe 's/"text"://; s/^"//; s/",$//'. (Ik deed dit voor deze analyse.)

Aan alle haters die erop staan dat je een echte JSON-parser moet gebruiken — ja, dat is essentieel voor correctheid, maar

  1. Als je een heel snelle analyse wilt doen, zoals het tellen van waarden om fouten in het opschonen van gegevens te controleren of een algemeen gevoel voor de gegevens te krijgen, is het sneller om iets op de opdrachtregel uit te drukken. Een editor openen om een script te schrijven leidt af.
  2. grep -ois orden van grootte sneller dan de Python-standaard json-bibliotheek, tenminste als je dit doet voor tweets (die elk ~2 KB zijn). Ik weet niet zeker of dit alleen komt omdat jsontraag is (ik zou het eens met yajl moeten vergelijken); maar in principe zou een regex sneller moeten zijn omdat het een eindige toestand is en veel beter te optimaliseren is, in plaats van een parser die recursie moet ondersteunen, en in dit geval veel CPU-structuren besteedt aan het bouwen van structuren waar je niet om geeft. (Als iemand een eindige-toestandstransducer zou hebben geschreven die de juiste (diepte-beperkte) JSON-parsing deed, zou dat fantastisch zijn! In de tussentijd hebben we “grep -o”.)

Om onderhoudbare code te schrijven, gebruik ik altijd een echte parseerbibliotheek. Ik heb jsawkniet geprobeerd, maar als het goed werkt, zou dat punt 1 aanpakken.

Een laatste, gekkere oplossing: ik heb een script geschreven dat Python jsongebruikt en de gewenste sleutels extraheert in door tabs gescheiden kolommen; dan pijp ik door een wrapper rond awkdie benoemde toegang tot kolommen toestaat. Hier: de json2tsv- en tsvawk-scripts. Dus voor dit voorbeeld zou het zijn:

json2tsv id text < tweets.json | tsvawk '{print "tweet " $id " is: " $text}'

Deze aanpak is niet gericht op #2, is inefficiënter dan een enkel Python-script, en het is een beetje broos: het dwingt normalisatie van nieuwe regels en tabbladen in stringwaarden af, om leuk te spelen met awk’s veld/record-gescheiden weergave van de wereld. Maar het laat je wel op de commandoregel blijven, met meer correctheid dan grep -o.


Antwoord 3, autoriteit 13%

Aangezien sommige van de aanbevelingen hier (vooral in de opmerkingen) het gebruik van Python suggereerden, was ik teleurgesteld dat ik geen voorbeeld kon vinden.

Dus hier is een one-liner om een enkele waarde uit sommige JSON-gegevens te halen. Het gaat ervan uit dat u de gegevens (van ergens) doorstuurt en dus nuttig zou moeten zijn in een scriptcontext.

echo '{"hostname":"test","domainname":"example.com"}' | python -c 'import json,sys;obj=json.load(sys.stdin);print obj["hostname"]'

Antwoord 4, autoriteit 10%

In navolging van MartinR en Boecko’s voorbeeld:

$ curl -s 'http://twitter.com/users/username.json' | python -mjson.tool

Dat geeft je een extreem grep-vriendelijke output. Erg handig:

$ curl -s 'http://twitter.com/users/username.json' | python -mjson.tool | grep my_key

Antwoord 5, autoriteit 10%

U kunt gewoon download jqbinair bestand voor uw platformen uitvoeren (chmod +x jq):

$ curl 'https://twitter.com/users/username.json' | ./jq -r '.name'

Het extraheert het kenmerk "name"uit het json-object.

jqhomepagezegt dat het is als sedvoor JSON-gegevens .


Antwoord 6, autoriteit 8%

Node.js gebruiken

Als het systeem nodeheeft geïnstalleerd, is het mogelijk om de -pprint te gebruiken en -escriptvlaggen te evalueren met JSON.parseom elke gewenste waarde eruit te halen.

Een eenvoudig voorbeeld waarbij de JSON-string { "foo": "bar" }wordt gebruikt en de waarde van “foo” wordt verwijderd:

$ node -pe 'JSON.parse(process.argv[1]).foo' '{ "foo": "bar" }'
bar

Omdat we toegang hebben tot caten andere hulpprogramma’s, kunnen we dit gebruiken voor bestanden:

$ node -pe 'JSON.parse(process.argv[1]).foo' "$(cat foobar.json)"
bar

Of een ander formaat zoals een URL die JSON bevat:

$ node -pe 'JSON.parse(process.argv[1]).name' "$(curl -s https://api.github.com/users/trevorsenior)"
Trevor Senior

Antwoord 7, autoriteit 8%

Gebruik Python’s JSON-ondersteuningin plaats van awk!

Zoiets:

curl -s http://twitter.com/users/username.json | \
    python -c "import json,sys;obj=json.load(sys.stdin);print(obj['name']);"

Antwoord 8, autoriteit 5%

Je hebt gevraagd hoe je jezelf in de voet kunt schieten en ik ben hier om de munitie te leveren:

curl -s 'http://twitter.com/users/username.json' | sed -e 's/[{}]/''/g' | awk -v RS=',"' -F: '/^text/ {print $2}'

Je zou tr -d '{}'kunnen gebruiken in plaats van sed. Maar ze helemaal weglaten lijkt ook het gewenste effect te hebben.

Als je de buitenste aanhalingstekens wilt verwijderen, pijp je het resultaat van het bovenstaande door sed 's/\(^"\|"$\)//g'

Ik denk dat anderen voldoende alarm hebben geslagen. Ik sta klaar met een mobiele telefoon om een ambulance te bellen. Vuur als je klaar bent.


Antwoord 9, autoriteit 3%

Bash gebruiken met Python

Maak een bash-functie in je .bash_rc-bestand

function getJsonVal () { 
    python -c "import json,sys;sys.stdout.write(json.dumps(json.load(sys.stdin)$1))"; 
}

Dan

$ curl 'http://twitter.com/users/username.json' | getJsonVal "['text']"
My status
$ 

Hier is dezelfde functie, maar met foutcontrole.

function getJsonVal() {
   if [ \( $# -ne 1 \) -o \( -t 0 \) ]; then
       cat <<EOF
Usage: getJsonVal 'key' < /tmp/
 -- or -- 
 cat /tmp/input | getJsonVal 'key'
EOF
       return;
   fi;
   python -c "import json,sys;sys.stdout.write(json.dumps(json.load(sys.stdin)$1))";
}

Waar $# -ne 1 zorgt voor minimaal 1 invoer, en -t 0 ervoor zorgt dat je omleidt vanaf een pijp.

Het leuke van deze implementatie is dat je toegang hebt tot geneste json-waarden en in ruil daarvoor json krijgt! =)

Voorbeeld:

$ echo '{"foo": {"bar": "baz", "a": [1,2,3]}}' |  getJsonVal "['foo']['a'][1]"
2

Als u echt wilt luisteren, kunt u de gegevens behalen:

function getJsonVal () { 
    python -c "import json,sys;sys.stdout.write(json.dumps(json.load(sys.stdin)$1, sort_keys=True, indent=4))"; 
}
$ echo '{"foo": {"bar": "baz", "a": [1,2,3]}}' |  getJsonVal "['foo']"
{
    "a": [
        1, 
        2, 
        3
    ], 
    "bar": "baz"
}

Antwoord 10, Autoriteit 3%

UPDATE (2020)

Mijn grootste probleem met externe tools (b.v. Python) was dat u te maken hebt met pakketmanagers en afhankelijkheden om ze te installeren.

Nu, nu dat we jqhebben als een stand-alone, statische tool die eenvoudig te installeren is om cross-platform te installeren via Github-releases en webi (webinstall.dev/jq ), i ‘ D Beveel het aan dat:

Mac, Linux:

curl -sS https://webinstall.dev/jq | bash

Windows 10:

curl.exe -A MS https://webinstall.dev/jq | powershell

origineel (2011)

ticktick is een JSON PARDER geschreven in bash (& lt; 250 regels code)

Hier is de snippit van de auteur uit zijn artikel, Stel je een wereld voor waar Bash JSON ondersteunt:

#!/bin/bash
. ticktick.sh
``  
  people = { 
    "Writers": [
      "Rod Serling",
      "Charles Beaumont",
      "Richard Matheson"
    ],  
    "Cast": {
      "Rod Serling": { "Episodes": 156 },
      "Martin Landau": { "Episodes": 2 },
      "William Shatner": { "Episodes": 2 } 
    }   
  }   
``  
function printDirectors() {
  echo "  The ``people.Directors.length()`` Directors are:"
  for director in ``people.Directors.items()``; do
    printf "    - %s\n" ${!director}
  done
}   
`` people.Directors = [ "John Brahm", "Douglas Heyes" ] ``
printDirectors
newDirector="Lamont Johnson"
`` people.Directors.push($newDirector) ``
printDirectors
echo "Shifted: "``people.Directors.shift()``
printDirectors
echo "Popped: "``people.Directors.pop()``
printDirectors

Antwoord 11, autoriteit 2%

Met behulp van standaard Unix-tools die beschikbaar zijn op de meeste distro’s. Werkt ook goed met backslashes (\) en aanhalingstekens (“)

WAARSCHUWING: dit komt niet in de buurt van de kracht van jq en werkt alleen met zeer eenvoudige JSON-objecten. Het is een poging om de oorspronkelijke vraag te beantwoorden en in situaties waarin je geen extra tools kunt installeren.

function parse_json()
{
    echo $1 | \
    sed -e 's/[{}]/''/g' | \
    sed -e 's/", "/'\",\"'/g' | \
    sed -e 's/" ,"/'\",\"'/g' | \
    sed -e 's/" , "/'\",\"'/g' | \
    sed -e 's/","/'\"---SEPERATOR---\"'/g' | \
    awk -F=':' -v RS='---SEPERATOR---' "\$1~/\"$2\"/ {print}" | \
    sed -e "s/\"$2\"://" | \
    tr -d "\n\t" | \
    sed -e 's/\\"/"/g' | \
    sed -e 's/\\\\/\\/g' | \
    sed -e 's/^[ \t]*//g' | \
    sed -e 's/^"//'  -e 's/"$//'
}
parse_json '{"username":"john, doe","email":"[email protected]"}' username
parse_json '{"username":"john doe","email":"[email protected]"}' email
--- outputs ---
john, doe
[email protected]

Antwoord 12, autoriteit 2%

JSON parseren met PHP CLI

Ongetwijfeld off-topic, maar aangezien voorrang heerst, blijft deze vraag onvolledig zonder een vermelding van onze betrouwbare en trouwe PHP, heb ik gelijk?

Hetzelfde voorbeeld-JSON gebruiken, maar laten we het toewijzen aan een variabele om onduidelijkheid te verminderen.

$ export JSON='{"hostname":"test","domainname":"example.com"}'

Nu voor PHP-goedheid, met behulp van file_get_contentsen de php://stdinstream-wrapper.

$ echo $JSON|php -r 'echo json_decode(file_get_contents("php://stdin"))->hostname;'

of zoals aangegeven met behulp van fgetsen de reeds geopende stream op CLI-constante STDIN.

$ echo $JSON|php -r 'echo json_decode(fgets(STDIN))->hostname;'

nVreugde!


Antwoord 13

Als iemand alleen waarden uit eenvoudige JSON-objecten wil extraheren zonder geneste structuren, is het mogelijk om reguliere expressies te gebruiken zonder de bash te verlaten.

Hier is een functie die ik heb gedefinieerd met bash-reguliere expressies op basis van de JSON-standaard:

function json_extract() {
  local key=$1
  local json=$2
  local string_regex='"([^"\]|\\.)*"'
  local number_regex='-?(0|[1-9][0-9]*)(\.[0-9]+)?([eE][+-]?[0-9]+)?'
  local value_regex="${string_regex}|${number_regex}|true|false|null"
  local pair_regex="\"${key}\"[[:space:]]*:[[:space:]]*(${value_regex})"
  if [[ ${json} =~ ${pair_regex} ]]; then
    echo $(sed 's/^"\|"$//g' <<< "${BASH_REMATCH[1]}")
  else
    return 1
  fi
}

Voorbehoud: objecten en arrays worden niet ondersteund als waarde, maar alle andere waardetypen die in de standaard zijn gedefinieerd, worden wel ondersteund. Een paar wordt ook gematcht, ongeacht hoe diep het JSON-document is, zolang het maar exact dezelfde sleutelnaam heeft.

Het voorbeeld van OP gebruiken:

$ json_extract text "$(curl 'http://twitter.com/users/username.json')"
My status
$ json_extract friends_count "$(curl 'http://twitter.com/users/username.json')"
245

Antwoord 14

Versie die Ruby en http://flori.github.com/json/

$ < file.json ruby -e "require 'rubygems'; require 'json'; puts JSON.pretty_generate(JSON[STDIN.read]);"

of beknopter:

$ < file.json ruby -r rubygems -r json -e "puts JSON.pretty_generate(JSON[STDIN.read]);"

Antwoord 15

Helaas retourneert het best gestemde antwoord dat grepgebruikt de volledigeovereenkomst die niet werkte in mijn scenario, maar als je weet dat het JSON-formaat constant blijft, kun je gebruik lookbehinden lookaheadom alleen de gewenste waarden te extraheren.

# echo '{"TotalPages":33,"FooBar":"he\"llo","anotherValue":100}' | grep -Po '(?<="FooBar":")(.*?)(?=",)'
he\"llo
# echo '{"TotalPages":33,"FooBar":"he\"llo","anotherValue":100}' | grep -Po '(?<="TotalPages":)(.*?)(?=,)'
33
#  echo '{"TotalPages":33,"FooBar":"he\"llo","anotherValue":100}' | grep -Po '(?<="anotherValue":)(.*?)(?=})'
100

Antwoord 16

Er is een eenvoudigere manier om een eigenschap uit een json-tekenreeks te halen. Gebruik een package.json-bestand als voorbeeld, probeer dit:

#!/usr/bin/env bash
my_val="$(json=$(<package.json) node -pe "JSON.parse(process.env.json)['version']")"

We gebruiken process.envomdat dit de inhoud van het bestand als een tekenreeks in node.js krijgt zonder enig risico dat schadelijke inhoud aan hun citaten ontsnapt en als code wordt geparseerd.


Antwoord 17

Nu Powershell cross-platform is, dacht ik dat ik me een weg zou banen, omdat ik vind dat het redelijk intuïtief en extreem eenvoudig is.

curl -s 'https://api.github.com/users/lambda' | ConvertFrom-Json 

ConvertFrom-Json converteert de JSON naar een aangepast Powershell-object, zodat u vanaf dat moment eenvoudig met de eigenschappen kunt werken. Als u bijvoorbeeld alleen de eigenschap ‘id’ wilt hebben, doet u dit:

curl -s 'https://api.github.com/users/lambda' | ConvertFrom-Json | select -ExpandProperty id

Als je het hele ding vanuit Bash wilde aanroepen, dan zou je het als volgt moeten aanroepen:

powershell 'curl -s "https://api.github.com/users/lambda" | ConvertFrom-Json'

Natuurlijk is er een pure Powershell-manier om het zonder krul te doen, wat zou zijn:

Invoke-WebRequest 'https://api.github.com/users/lambda' | select -ExpandProperty Content | ConvertFrom-Json

Ten slotte is er ook ‘ConvertTo-Json’ waarmee een aangepast object net zo gemakkelijk naar JSON wordt geconverteerd. Hier is een voorbeeld:

(New-Object PsObject -Property @{ Name = "Tester"; SomeList = @('one','two','three')}) | ConvertTo-Json

Wat een mooie JSON zou opleveren zoals deze:

{
"Name":  "Tester",
"SomeList":  [
                 "one",
                 "two",
                 "three"
             ]

}

Toegegeven, het gebruik van een Windows-shell op Unix is enigszins heiligschennend, maar Powershell is echt goed in sommige dingen, en het ontleden van JSON en XML zijn er een paar van. Dit is de GitHub-pagina voor de platformonafhankelijke versie https://github.com/PowerShell/PowerShell


Antwoord 18

Ik kan geen van de antwoorden hier gebruiken. Geen beschikbare jq, geen shell-arrays, geen declareren, geen grep -P, geen blik achter en vooruit, geen Python, geen Perl, geen Ruby, nee – zelfs geen Bash… De resterende antwoorden werken gewoon niet goed. JavaScript klonk bekend, maar het blikje zegt Nescaffe – dus het is ook een no go 🙂 Zelfs als het beschikbaar zou zijn, voor mijn simpele behoefte, zouden ze overdreven en traag zijn.

Toch is het uiterst belangrijk voor mij om veel variabelen uit het json-geformatteerde antwoord van mijn modem te halen. Ik doe het in een handomdraai met een zeer ingekorte BusyBox op mijn routers! Geen problemen om alleen awk te gebruiken: stel gewoon scheidingstekens in en lees de gegevens. Voor een enkele variabele is dat alles!

awk 'BEGIN { FS="\""; RS="," }; { if ($2 == "login") {print $4} }' test.json

Weet je nog dat ik geen arrays heb? Ik moest binnen de awk geparseerde gegevens toewijzen aan de 11 variabelen die ik nodig heb in een shellscript. Waar ik ook keek, er werd gezegd dat het een onmogelijke missie was. Ook daar geen probleem mee.

Mijn oplossing is eenvoudig. Deze code zal:
1) ontleden .json-bestand van de vraag (eigenlijk heb ik een werkend gegevensvoorbeeld geleend van het meest geüpdatete antwoord) en kies de geciteerde gegevens, plus
2) creëer shell-variabelen vanuit de awk door vrije benoemde shell-variabelen toe te wijzen.

eval $( curl -s 'https://api.github.com/users/lambda' | 
awk ' BEGIN { FS="\""; RS="," };
{
    if ($2 == "login") { print "Login=\""$4"\"" }
    if ($2 == "name") { print "Name=\""$4"\"" }
    if ($2 == "updated_at") { print "Updated=\""$4"\"" }
}' )
echo "$Login, $Name, $Updated"

Geen problemen met lege plekken binnenin. In mijn gebruik parseert dezelfde opdracht een lange uitvoer met één regel. Aangezien eval wordt gebruikt, is deze oplossing alleen geschikt voor vertrouwde gegevens. Het is eenvoudig aan te passen om niet-geciteerde gegevens op te halen. Voor een groot aantal variabelen kan marginale snelheidswinst worden bereikt met else if. Gebrek aan array betekent natuurlijk: geen meerdere records zonder extra gehannes. Maar waar arrays beschikbaar zijn, is het aanpassen van deze oplossing een eenvoudige taak.

@maikel sed antwoord werkt bijna (maar ik kan er geen commentaar op geven). Voor mijn mooi opgemaakte gegevens – het werkt. Niet zozeer met het hier gebruikte voorbeeld (ontbrekende aanhalingstekens gooien het weg). Het is ingewikkeld en moeilijk te wijzigen. Bovendien hou ik er niet van om 11 aanroepen te moeten doen om 11 variabelen te extraheren. Waarom? Ik heb 100 loops getimed en 9 variabelen geëxtraheerd: de sed-functie duurde 48,99 sec en mijn oplossing duurde 0,91 sec! Niet eerlijk? Slechts een enkele extractie van 9 variabelen: 0,51 vs. 0,02 sec.


Antwoord 19

Dit is weer een bash& pythonhybride antwoord. Ik heb dit antwoord gepost omdat ik complexere JSON-uitvoer wilde verwerken, maar de complexiteit van mijn bash-toepassing wilde verminderen. Ik wil het volgende JSON-object van http://www.arcgis openen. com/sharing/rest/info?f=jsonin bash:

{
  "owningSystemUrl": "http://www.arcgis.com",
  "authInfo": {
    "tokenServicesUrl": "https://www.arcgis.com/sharing/rest/generateToken",
    "isTokenBasedSecurity": true
  }
}

In het volgende voorbeeld heb ik mijn eigen implementatie gemaakt van jqen unquotedoor gebruik te maken van python. U zult merken dat zodra we het python-object van jsonnaar een python-woordenboek importeren, we de python-syntaxis kunnen gebruiken om door het woordenboek te navigeren. Om door het bovenstaande te navigeren, is de syntaxis:

  • data
  • data[ "authInfo" ]
  • data[ "authInfo" ][ "tokenServicesUrl" ]

Door magie in bash te gebruiken, laten we dataweg en leveren we alleen de python-tekst rechts van data, d.w.z.

  • jq
  • jq '[ "authInfo" ]'
  • jq '[ "authInfo" ][ "tokenServicesUrl" ]'

Let op, zonder parameters fungeert jqals een JSON-pretifier. Met parameters kunnen we de python-syntaxis gebruiken om alles wat we willen uit het woordenboek te halen, inclusief het navigeren door subwoordenboeken en array-elementen.

Hier zijn de bashpythonhybride functies:

#!/bin/bash -xe
jq_py() {
  cat <<EOF
import json, sys
data = json.load( sys.stdin )
print( json.dumps( data$1, indent = 4 ) )
EOF
}
jq() {
  python -c "$( jq_py "$1" )"
}
unquote_py() {
  cat <<EOF
import json,sys
print( json.load( sys.stdin ) )
EOF
}
unquote() {
  python -c "$( unquote_py )"
}

Hier is een voorbeeld van het gebruik van de bashpython-functies:

curl http://www.arcgis.com/sharing/rest/info?f=json | tee arcgis.json
# {"owningSystemUrl":"https://www.arcgis.com","authInfo":{"tokenServicesUrl":"https://www.arcgis.com/sharing/rest/generateToken","isTokenBasedSecurity":true}}
cat arcgis.json | jq
# {
#     "owningSystemUrl": "https://www.arcgis.com",
#     "authInfo": {
#         "tokenServicesUrl": "https://www.arcgis.com/sharing/rest/generateToken",
#         "isTokenBasedSecurity": true
#     }
# }
cat arcgis.json | jq '[ "authInfo" ]'
# {
#     "tokenServicesUrl": "https://www.arcgis.com/sharing/rest/generateToken",
#     "isTokenBasedSecurity": true
# }
cat arcgis.json | jq '[ "authInfo" ][ "tokenServicesUrl" ]'
# "https://www.arcgis.com/sharing/rest/generateToken"
cat arcgis.json | jq '[ "authInfo" ][ "tokenServicesUrl" ]' | unquote
# https://www.arcgis.com/sharing/rest/generateToken

Antwoord 20

Je kunt zoiets proberen –

curl -s 'http://twitter.com/users/jaypalsingh.json' | 
awk -F=":" -v RS="," '$1~/"text"/ {print}'

Antwoord 21

Iemand die ook xml-bestanden heeft, wil misschien mijn Xidelbekijken. Het is een cli, afhankelijkheidsvrije JSONiq-processor. (d.w.z. het ondersteunt ook XQuery voor xml- of json-verwerking)

Het voorbeeld in de vraag zou zijn:

xidel -e 'json("http://twitter.com/users/username.json")("name")'

Of met mijn eigen, niet-standaard extensiesyntaxis:

xidel -e 'json("http://twitter.com/users/username.json").name'

Antwoord 22

U kunt jshongebruiken:

curl 'http://twitter.com/users/username.json' | jshon -e text

Antwoord 23

Er is ook een zeer eenvoudige maar krachtige JSON CLI-verwerkingstool fxhttps://github.com/antonmedv/fx

Voorbeelden

Gebruik anonieme functie:

$ echo '{"key": "value"}' | fx "x => x.key"
value

Als u de anonieme functie param => …, wordt de code automatisch omgezet in een anonieme functie. En u kunt toegang krijgen tot JSON met dit trefwoord:

$ echo '[1,2,3]' | fx "this.map(x => x * 2)"
[2, 4, 6]

Of gebruik ook gewoon de puntsyntaxis:

$ echo '{"items": {"one": 1}}' | fx .items.one
1

U kunt een willekeurig aantal anonieme functies doorgeven om JSON te verminderen:

$ echo '{"items": ["one", "two"]}' | fx "this.items" "this[1]"
two

U kunt bestaande JSON bijwerken met de spread-operator:

$ echo '{"count": 0}' | fx "{...this, count: 1}"
{"count": 1}

Gewoon JavaScript. U hoeft geen nieuwe syntaxis te leren.


UPDATE 06-11-2018

fxheeft nu een interactieve modus (!)

https://github.com/antonmedv/fx


Antwoord 24

hier is een manier waarop je het kunt doen met awk

curl -sL 'http://twitter.com/users/username.json' | awk -F"," -v k="text" '{
    gsub(/{|}/,"")
    for(i=1;i<=NF;i++){
        if ( $i ~ k ){
            print $i
        }
    }
}'

Antwoord 25

Voor complexere JSON-parsing raad ik aan de python jsonpath-module te gebruiken (door Stefan Goessner) –

  1. Installeer het –

sudo easy_install -U jsonpath

  1. Gebruik het –

Voorbeeld file.json (van http://goessner.net/articles/JsonPath) –

{ "store": {
    "book": [ 
      { "category": "reference",
        "author": "Nigel Rees",
        "title": "Sayings of the Century",
        "price": 8.95
      },
      { "category": "fiction",
        "author": "Evelyn Waugh",
        "title": "Sword of Honour",
        "price": 12.99
      },
      { "category": "fiction",
        "author": "Herman Melville",
        "title": "Moby Dick",
        "isbn": "0-553-21311-3",
        "price": 8.99
      },
      { "category": "fiction",
        "author": "J. R. R. Tolkien",
        "title": "The Lord of the Rings",
        "isbn": "0-395-19395-8",
        "price": 22.99
      }
    ],
    "bicycle": {
      "color": "red",
      "price": 19.95
    }
  }
}

Ontdek het (extraheer alle boektitels met prijs < 10) –

$ cat file.json | python -c "import sys, json, jsonpath; print '\n'.join(jsonpath.jsonpath(json.load(sys.stdin), 'store.book[?(@.price < 10)].title'))"

Zal uitvoeren –

Sayings of the Century
Moby Dick

OPMERKING: de bovenstaande opdrachtregel bevat geen foutcontrole. voor een volledige oplossing met foutcontrole moet u een klein python-script maken en de code inpakken met try-behalve.


Antwoord 26

Als je phphebt:

php -r 'var_export(json_decode(`curl http://twitter.com/users/username.json`, 1));'

Bijvoorbeeld:

we hebben bronnen die json voorzien van iso-codes voor landen: http://country.io/iso3.jsonhttp://country.io/iso3.jsonen we kunnen het gemakkelijk zien in een schelp met krul:

curl http://country.io/iso3.json

maar het ziet er niet erg handig uit, en niet leesbaar, beter json ontleden en leesbare structuur zien:

php -r 'var_export(json_decode(`curl http://country.io/iso3.json`, 1));'

Deze code drukt iets af als:

array (
  'BD' => 'BGD',
  'BE' => 'BEL',
  'BF' => 'BFA',
  'BG' => 'BGR',
  'BA' => 'BIH',
  'BB' => 'BRB',
  'WF' => 'WLF',
  'BL' => 'BLM',
  ...

als je geneste arrays hebt, ziet deze uitvoer er veel beter uit…

Hopelijk helpt dit…


Antwoord 27

Ik heb dit gedaan door een json-antwoord voor een bepaalde waarde als volgt te “parseren”:

curl $url | grep $var | awk '{print $2}' | sed s/\"//g 

Het is duidelijk dat $url hier de twitter-url zou zijn, en $var zou “tekst” zijn om het antwoord voor die var te krijgen.

Eigenlijk denk ik dat het enige wat ik doe dat de OP heeft weggelaten, grep is voor de regel met de specifieke variabele die hij zoekt. Awk pakt het tweede item op de lijn en met sed verwijder ik de aanhalingstekens.

Iemand die slimmer is dan ik, kan waarschijnlijk het hele denken doen met awk of grep.

Nu, je zou het allemaal kunnen doen met alleen sed:

curl $url | sed '/text/!d' | sed s/\"text\"://g | sed s/\"//g | sed s/\ //g

dus geen awk, geen grep…Ik weet niet waarom ik daar niet eerder aan gedacht heb. Hmmm…


Antwoord 28

Het ontleden van JSON is pijnlijk in een shellscript. Maak met een meer geschikte taal een tool die JSON-kenmerken extraheert op een manier die consistent is met shellscriptingconventies. U kunt uw nieuwe tool gebruiken om het directe shellscripting-probleem op te lossen en het vervolgens aan uw kit toevoegen voor toekomstige situaties.

Overweeg bijvoorbeeld een tool jsonlookupzodat als ik zeg jsonlookup access token idhet het attribuut idteruggeeft dat in het attribuut is gedefinieerd tokengedefinieerd binnen het attribuut toegangvan stdin, wat vermoedelijk JSON-gegevens zijn. Als het kenmerk niet bestaat, retourneert het hulpprogramma niets (afsluitstatus 1). Als het parseren mislukt, verlaat dan status 2 en een bericht naar stderr. Als het opzoeken lukt, drukt de tool de waarde van het attribuut af.

Nadat je een unix-tool hebt gemaakt met het precieze doel om JSON-waarden te extraheren, kun je deze gemakkelijk gebruiken in shellscripts:

access_token=$(curl <some horrible crap> | jsonlookup access token id)

Elke taal is voldoende voor de implementatie van jsonlookup. Hier is een vrij beknopte pythonversie:

#!/usr/bin/python                                                               
import sys
import json
try: rep = json.loads(sys.stdin.read())
except:
    sys.stderr.write(sys.argv[0] + ": unable to parse JSON from stdin\n")
    sys.exit(2)
for key in sys.argv[1:]:
    if key not in rep:
        sys.exit(1)
    rep = rep[key]
print rep

Antwoord 29

Een two-liner die python gebruikt. Het werkt vooral goed als je een enkel .sh-bestand schrijft en je niet afhankelijk wilt zijn van een ander .py-bestand. Het maakt ook gebruik van het gebruik van pipe |. echo "{\"field\": \"value\"}"kan worden vervangen door alles dat een json naar de stdout afdrukt.

echo "{\"field\": \"value\"}" | python -c 'import sys, json
print(json.load(sys.stdin)["field"])'

Antwoord 30

Dit is een goede usecase voor pythonpy:

curl 'http://twitter.com/users/username.json' | py 'json.load(sys.stdin)["name"]'

Other episodes