JSON'u Unix araçlarıyla ayrıştırma


879

Böyle bir kıvırmak isteği döndü JSON ayrıştırmak çalışıyorum:

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]}'

Yukarıdakiler JSON'u alanlara ayırır, örneğin:

% ...
"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
% ...

Belirli bir alanı (ile belirtilen -v k=text) nasıl yazdırabilirim ?


5
Erm bu iyi json btw ayrıştırma ... peki dizelerde kaçış karakterleri hakkında ... vb SO (perl cevap bile ...) bu bir python cevap var mı?
martinr

51
Birisi "sorun X, diğer Y dili ile kolayca çözülebilir" dediğinde, " araç kutumda çivi sürmek için sadece bir kaya var ... neden başka bir şeyle uğraşalım?"
BryanH

22
@BryanH: Bazen Y dili, Y'yi öneren kişinin kaç dil bildiğine bakılmaksızın belirli X problemini çözmek için daha donanımlı olabilir .
jfs

15
Biraz geç, ama işte gidiyor. grep -Po '"'"version"'"\s*:\s*"\K([^"]*)' package.json. Bu, görevi kolayca ve sadece grep ile çözer ve basit JSON'lar için mükemmel çalışır. Karmaşık JSON'lar için uygun bir ayrıştırıcı kullanmalısınız.
diosney

2
@auser, başlığında "sed ve awk ile" bir düzenlemeyi "UNIX araçlarıyla" olarak değiştirir misiniz?
Charles Duffy

Yanıtlar:


1127

JSON'u komut satırından değiştirmek için özel olarak tasarlanmış bir dizi araç vardır ve Awk ile yapmaktan çok daha kolay ve daha güvenilir olacaktır, örneğin jq:

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

Bunu, jsonmodülü kullanarak Python gibi sisteminizde zaten yüklü olan araçlarla da yapabilir ve böylece uygun bir JSON ayrıştırıcısının avantajına sahipken ekstra bağımlılıklardan kaçınabilirsiniz. Aşağıdakiler, orijinal JSON'un kodlanması gereken ve çoğu modern terminalin de kullandığı UTF-8'i kullanmak istediğinizi varsayar:

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']"

Tarihsel notlar

Bu cevap başlangıçta hala çalışması gereken, ancak kullanımı bir Python yorumlayıcısından daha az yaygın olan bağımsız bir JavaScript yorumlayıcısına bağlı olarak biraz daha zahmetli olan jsawk'ı önerdi , bu jqnedenle yukarıdaki cevaplar muhtemelen tercih edilir:

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

Bu cevap başlangıçta sorudan Twitter API'sını da kullandı, ancak API artık çalışmaz, test etmek için örnekleri kopyalamayı zorlaştırıyor ve yeni Twitter API'sı API anahtarları gerektiriyor, bu yüzden GitHub API'sını kullanmaya başladım. API anahtarları olmadan kolayca kullanılabilir. Orijinal soru için ilk cevap:

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

7
@thrau +1. jq depoda mevcuttur ve kullanımı çok kolaydır, bu yüzden jsawk'tan çok daha iyidir. Her ikisini de birkaç dakika test ettim, jq bu savaşı kazandı
Szymon Sadło

1
Python 2'de, çıkışı başka bir komuta boru kullanıyorsanız, bir boruda Python kullandığınız için printdeyimin her zaman ASCII'ye kodlanacağını unutmayın. PYTHONIOENCODING=<desired codec>Terminalinize uygun farklı bir çıkış kodlaması ayarlamak için komuta ekleyin . Python 3'te bu durumda varsayılan UTF-8'dir ( print() işlevi kullanarak ).
Martijn Pieters

1
Demlemek jq ile OSX üzerine jq yükleyin
Andy Fraley

1
curl -seşdeğer curl --silent, buna karşılık dize tırnakları olmadan jq -ranlamına gelir jq --raw-output.
Serge Stroobandt

python -c "içe aktarma istekleri; r = requests.get (' api.github.com/users/lambda');print r.json () [' ad '];" . En basit!
NotTooTechy

277

Belirli bir anahtarın değerlerini hızlı bir şekilde ayıklamak için, kişisel olarak yalnızca normal ifadenin eşleşmesini döndüren "grep -o" kullanmayı seviyorum. Örneğin, "metin" alanını tweet'ten almak için aşağıdakine benzer:

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

Bu normal ifade düşündüğünüzden daha sağlamdır; örneğin, virgül ve içlerinde tırnak işareti bulunan dizelerle iyi ilgilenir. Biraz daha fazla çalışma ile, eğer atomikse, değeri çıkarmak için garanti edilen bir tane yapabilirsin. (İç içe yerleştirme varsa, normal bir regex bunu yapamaz.)

Ve daha bir temiz için (dize ait orijinal kaçışa tutarak olsa) gibi bir şey kullanabilirsiniz: | perl -pe 's/"text"://; s/^"//; s/",$//'. (Bunu bu analiz için yaptım .)

Israr eden tüm nefret edenler için gerçek bir JSON ayrıştırıcı kullanmalısınız - evet, doğruluk için gereklidir, ancak

  1. Veri temizleme hatalarını kontrol etmek veya veriler hakkında genel bir fikir edinmek için değerleri saymak gibi gerçekten hızlı bir analiz yapmak için, komut satırında bir şeyi vurmak daha hızlıdır. Bir senaryo yazmak için bir editör açmak dikkat dağıtıcıdır.
  2. grep -ojsonen azından tweetler için (her biri ~ 2 KB olan) Python standart kütüphanesinden daha hızlı büyüklük sıralarıdır . Bunun sadece jsonyavaş olduğundan emin değilim (bazen yajl ile karşılaştırmalıyım); ancak prensip olarak, normal ifadenin daha hızlı olması gerekir, çünkü özyinelemeyi desteklemek zorunda olan bir ayrıştırıcı yerine sonlu durum ve çok daha optimize edilebilir ve bu durumda umursamadığınız yapılar için çok sayıda CPU bina ağacı harcar. (Birisi uygun (derinlik sınırlı) JSON ayrıştırma yapan bir sonlu durum dönüştürücü yazdıysa, bu harika olurdu! Bu arada "grep -o" var.)

Korumalı kod yazmak için her zaman gerçek bir ayrıştırma kitaplığı kullanırım. Ben denemedim jsawk , ama iyi çalışırsa, o nokta 1. ele alınacak.

Son bir, wackier çözümü: Python kullanan jsonve istediğiniz tuşları sekmeyle ayrılmış sütunlara ayıklayan bir komut dosyası yazdım ; daha sonra awk, sütunlara adlandırılmış erişim sağlayan bir sargı içinden geçiyorum . Burada: json2tsv ve tsvawk komut dosyaları . Yani bu örnek için:

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

Bu yaklaşım # 2'yi ele almaz, tek bir Python betiğinden daha verimsizdir ve biraz kırılgandır: awk'ın alan / kayıtla sınırlandırılmış görünümü ile güzel oynamak için dize değerlerindeki yeni satırların ve sekmelerin normalleşmesini zorlar. Ancak komut satırında kalmanıza izin verir, daha doğrudur grep -o.


11
Tam sayı değerlerini unuttun. grep -Po '"text":(\d*?,|.*?[^\\]",)'
Robert

3
Robert: Doğru, normal ifadem yalnızca bu alan için dize değerleri için yazılmıştır. Tamsayılar dediğiniz gibi eklenebilir. Tüm türleri istiyorsanız, daha fazlasını yapmalısınız: booleans, null. Ve diziler ve nesneler daha fazla iş gerektirir; standart regex'ler altında sadece derinlik sınırlıdır.
Brendan OConnor

9
1. jq .namekomut satırında çalışır ve "komut dosyası yazmak için bir düzenleyici açmayı" gerektirmez. 2. Normal
ifadenizin

6
ve sadece değerleri istiyorsanız sadece awk atabilirsiniz. | grep -Po '"text":.*?[^\\]",'|awk -F':' '{print $2}'
JeffCharter

34
OSX'de -Pseçenek eksik görünüyor . OSX 10.11.5 üzerinde test yaptım ve grep --versionöyleydi grep (BSD grep) 2.5.1-FreeBSD. Ben OSX "genişletilmiş regex" seçeneği ile çalışma var. Yukarıdaki komut grep -Eo '"text":.*?[^\\]",' tweets.json.
Jens

174

Burada bazı öneriler (esp yorumlarda) Python kullanımını önerdi temelinde, bir örnek bulmak için hayal kırıklığına uğradım.

Yani, bazı JSON verilerinden tek bir değer elde etmek için bir astar. Verileri (bir yerden) pipetlediğinizi varsayar ve bu nedenle bir komut dosyası bağlamında yararlı olmalıdır.

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

Bir bash işlevini kullanmak için aşağıdaki bu yanıtı geliştirdim: curl 'some_api' | getJsonVal 'key'
Joe Heyming

pythonpy( github.com/russell91/pythonpy hemen hemen her zaman daha iyi bir alternatiftir python -c, ancak pip ile yüklenmesi gerekir. sadece py --ji -x 'x[0]["hostname"]'json'u borulayın. olarak otomatik olarak içe aktarılanlarpy 'json.loads(sys.stdin)[0]["hostname"]'
RussellStewart

2
Teşekkürler! Daha hızlı ve kirli JSON ayrıştırma için bir bash işlevine sardım: jsonq() { python -c "import sys,json; obj=json.load(sys.stdin); print($1)"; }böylece yazabilirim: curl ...... | jsonq 'json.dumps([key["token"] for key in obj], indent=2)'& benzer korkunç şeyler daha fazla ... Btw, obj[0]gereksiz görünüyor, sadece objvarsayılan durumlarda Tamam görünüyor gibi görünüyor (?).
akavel

Teşekkürler. Bu saygıyı JSON'u baskıdan biraz daha iyi yaptım:jsonq() { python -c "import sys,json; obj=json.load(sys.stdin); sys.stdout.write(json.dumps($1))"; }
Adam K Dean

4
obj[0]ayrıştırılırken hataya neden olur { "port":5555 }. Çıkardıktan sonra iyi çalışıyor [0].
CyberEd

134

MartinR ve Boecko'nun liderliğini takiben:

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

Bu size son derece grep dostu bir çıktı verecektir. Çok uygun:

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

37
OP'nin istediği gibi belirli bir anahtarı nasıl çıkarırsınız?
juan

2
Şimdiye kadar en iyi cevap imho, çoğu dağıtım üzerine başka bir şey yüklemenize gerek yok ve yapabilirsiniz | grep field. Teşekkürler!
Andrea Richiardi

7
Yanılmıyorsam tüm bunlar JSON'u biçimlendirmektir. Bir xpath çözümü veya "JSON Pointer" temelli bir şey gibi, çağıranın çıktıdan belirli bir alanı seçmesine izin vermez.
Cheeso

4
Ben sadece bir anahtar değer çifti ile bitirmek, ama kendi başına değer değil.
christopher

1
jqgenellikle python varken kurulmaz. Ayrıca, bir kez senin Python da tüm yol gitmek ve ayrıştırmakimport json...
CpILL

125

Sadece olabilir indirmek jqiçin platformu için ikili ve run ( chmod +x jq):

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

"name"Json nesnesinden öznitelik ayıklar .

jqanasayfa bunun sedJSON verileri için olduğunu söylüyor .


27
Sadece kayıt için, jqinanılmaz bir araçtır.
hoss

2
Kabul. Bunu kullanmadığım için kabul edilen cevaptan jsawk ile kıyaslayamıyorum, ancak yerel deneyler için (bir aracın yüklenmesinin kabul edilebilir olduğu yerlerde) jq'yi tavsiye ederim. Aşağıda, bir dizinin her bir öğesini alan ve yeni bir JSON nesnesini seçilen verilerle sentezleyen biraz daha kapsamlı bir örnek verilmiştir:curl -s https://api.example.com/jobs | jq '.jobs[] | {id, o: .owner.username, dateCreated, s: .status.state}'
jbyler

2
Bunu sev. Çok hafif ve sade eski C olduğu için hemen hemen her yerde derlenebilir.
Benmj

1
En pratik olanı: üçüncü taraf kitaplıklarına ihtiyaç duymaz (jsawk yaparken) ve kurulumu kolaydır (OSX: brew install jq)
lauhub

1
Bu benim kullanım durumum için en pratik ve kolay uygulanabilir cevaptır. Ubuntu (14.04) sistemi için basit bir apt-get install jq aracı sistemime ekledi. Ben jq içine AWS CLI yanıtları JSON çıktı boru ve yanıt iç içe bazı anahtarları değerleri ayıklamak için harika çalışıyor.
Brandon K

105

Node.js kullanma

Sistemde varsa yüklendiğinde, gerekli olan herhangi bir değeri çekmek için -pprint ve -eevaulate script bayraklarını kullanmak mümkündür JSON.parse.

JSON dizesini kullanarak ve { "foo": "bar" }"foo" değerini dışarı çeken basit bir örnek :

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

Erişebileceğimiz catve diğer yardımcı programlara sahip olduğumuz için , bunu dosyalar için kullanabiliriz:

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

Veya JSON içeren bir URL gibi başka bir biçim:

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

1
Teşekkürler! ama benim durumumda sadece -e bayrağıyla çalışıyornode -p -e 'JSON.parse(process.argv[1]).foo' '{ "foo": "bar" }'
Rnd_d

33
Borular! curl -s https://api.github.com/users/trevorsenior | node -pe "JSON.parse(require('fs').readFileSync('/dev/stdin').toString()).name"
nicerobot

4
bu benim en sevdiğim çözüm; kendine özgü bir veri yapısını (JSON) ayrıştırmak için bir dil (javascript) kullanın. en doğru gibi görünüyor . Ayrıca - düğüm muhtemelen sistemde zaten mevcuttur ve jq'in ikili dosyaları ile (başka bir doğru seçim gibi görünüyor) geçiş yapmak zorunda kalmazsınız .
Eliran Malka

Bu bash komut dosyası işlevidir: # jsonv belirli bir öznitelik için json nesne değerini alır # first parametresi json belgesidir # ikinci parametre, değer döndürülmesi gereken özniteliktir get_json_attribute_value () {node -pe 'JSON.parse (süreç. argv [1]) [process.argv [2]] '"1 $" "2 $"}
Youness

6
Aşağıdakiler Node.js 10 ile çalışır:cat package.json | node -pe 'JSON.parse(fs.readFileSync(0)).version'
Ilya Boyandin

100

Kullanım Python'un JSON desteklemek yerine awk kullanmak!

Bunun gibi bir şey:

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

6
İyi bir yanıt bulmaya çalıştığım için affedin ...: Daha çok deneyeceğim. Partizanlık, çalkalamak için bir awk senaryosu yazmaktan daha fazlasını gerektirir!
martinr

9
Neden oneliner çözümünde obj değişkenini kullanıyorsunuz? Bu işe yaramaz ve yine de saklanmaz? Sen kullanarak daha az yazma json.load(sys.stdin)['"key']"gibi örnek olarak: curl -sL httpbin.org/ip | python -c "import json,sys; print json.load(sys.stdin)['origin']".
m3nda

65

Kendinizi ayağınızdan nasıl vuracağınızı sordunuz ve ben de cephane sağlamak için buradayım:

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

Bunun tr -d '{}'yerine kullanabilirsiniz sed. Ama onları tamamen dışarıda bırakmak da istenen etkiye sahip gibi görünüyor.

Dış tırnak işaretlerini çıkarmak istiyorsanız, yukarıdakilerin sonucunu sed 's/\(^"\|"$\)//g'

Bence başkaları yeterli alarm vermiş. Ambulans çağırmak için cep telefonuyla yanında olacağım. Hazır olduğunda ateşle.



3
Tüm cevapları okudum ve bu ekstra bağımlılık olmadan benim için mükemmel çalışıyor. +1
eth0

Aradığım şey buydu. Tek düzeltme - tırnak kaldırmak için sed komutu benim için işe yaramadı, bunun yerine sed 's / "// g' kullandım
AlexG

44

Bash'i Python ile kullanma

.Bash_rc dosyanızda bir bash işlevi oluşturun

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

Sonra

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

İşte aynı işlev, ancak hata denetimi ile.

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))";
}

$ # -Ne 1, en az 1 giriş ve -t 0 bir kanaldan yönlendirdiğinizden emin olur.

Bu uygulama hakkında güzel bir şey, iç içe json değerlerine erişmek ve karşılığında json alabilirsiniz olmasıdır! =)

Misal:

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

Gerçekten süslü olmak istiyorsanız, verileri oldukça yazdırabilirsiniz:

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"
}

Bash fonksiyonu olmayan tek astar:curl http://foo | python -c 'import json,sys;obj=json.load(sys.stdin);print obj["environment"][0]["name"]'
Cheeso

1
sys.stdout.write()hem python 2 hem de 3 ile çalışmasını istiyorsanız
Per Johansson

Ben system.stdout.write (obj $ 1) değiştirmek gerektiğini düşünüyorum. Bu şekilde şunu söyleyebilirsiniz: getJsonVal "['çevre'] ['ad']", @Cheeso örneği gibi
Joe Heyming

1
@Narek Bu durumda, şöyle görünecektir: fonksiyongetJsonVal() { py -x "json.dumps(json.loads(x)$1, sort_keys=True, indent=4)"; }
Joe Heyming

30

TickTick bash ile yazılmış bir JSON ayrıştırıcısıdır (<250 kod satırı)

Yazarın Snash'in makalesinden Bash'in JSON'u desteklediği bir dünya hayal edin :

#!/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

2
Burada tek sağlam saf-bas cevap olarak, bu daha fazla oyu hak ediyor.
Ed Randall

Bu kişi değişkenini tekrar json dizesine yazdırmanın herhangi bir yolu var mı? Bu son derece yararlı olurdu
Thomas Fournet

1
Sonunda Python veya diğer iğrenç yöntemleri önermeyen bir cevap ... Teşekkürler!
Akito

21

JSON'u PHP CLI ile Ayrıştırma

Tartışmasız konu dışı ama öncelikli hüküm sürdüğü için güvenilir ve sadık PHP'mizden bahsetmeden bu soru eksik kalıyor, doğru mu?

Aynı JSON örneğini kullanarak ancak belirsizliği azaltmak için bir değişkene atayalım.

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

Şimdi PHP iyiliği için, file_get_contents ve php: // stdin akışı sarmalayıcısını kullanarak.

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

veya CLI sabiti STDIN'de fgets ve halihazırda açılmış olan akım kullanılarak belirtildiği gibi .

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

Njoy!


Hatta kullanabilirsiniz $argnyerinefgets(STDIN)
IcanDivideBy0

Hata! $argn-E veya -R bayrağıyla çalışır ve yalnızca JSON içeriği bir satırdaysa ...
satırdaysa çalışır IcanDivideBy0

21

Yerel Bash sürümü: Ayrıca ters eğik çizgiler (\) ve tırnak işaretleri (") ile iyi çalışır

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":"john@doe.com"}' username
parse_json '{"username":"john doe","email":"john@doe.com"}' email

--- outputs ---

john, doe
johh@doe.com

Bu harika. Ancak JSON dizesi birden fazla e-posta anahtarı içeriyorsa, ayrıştırıcı john@doe.com çıktısı verir "" john@doe.com
rtc11


13

Ruby ve http://flori.github.com/json/ kullanan sürüm

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

veya daha kısaca:

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

3
Bu benim en sevdiğim;) BTW kütüphaneyi gerektirmek için ruby ​​-rjson ile kısa devre yapabilirsiniz
lucapette

Finalin ;Ruby'de gerekli olmadığını unutmayın (yalnızca normalde ayrı satırlarda yer alan ifadeleri tek bir satıra birleştirmek için kullanılır).
Zack Morris

11

Ne yazık ki en çok oy kullanan cevap, senaryomda çalışmayan tam eşleşmeyi grepdöndürür , ancak JSON biçiminin sabit kalacağını biliyorsanız, yalnızca istenen değerleri ayıklamak için lookbehind ve lookahead'i kullanabilirsiniz .

# 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

Sen asla aslında biliyor JSON sözlükte öğelerin sırasını. Tanımı gereğince sıralanmamıştır. Bu, kendi JSON ayrıştırıcınızı döndürmenin mahkum bir yaklaşım olmasının temel nedenlerinden biridir.
Üçlü

10

Birisi sadece iç içe geçmiş yapılara ihtiyaç duymadan basit JSON nesnelerinden değerleri çıkarmak istiyorsa, bash'tan çıkmadan bile düzenli ifadeler kullanmak mümkündür.

JSON standardına dayalı bash düzenli ifadeleri kullanarak tanımladığım bir fonksiyon :

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
}

Uyarılar: nesneler ve diziler değer olarak desteklenmez, ancak standartta tanımlanan diğer tüm değer türleri desteklenir. Ayrıca, JSON belgesinde tam olarak aynı anahtar adına sahip olduğu sürece bir çift eşleştirilir.

OP örneğini kullanarak:

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

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

Helder Pereira bu işlevle iç içe özellik değerleri çıkarabilir miyiz?
vsbehere

8

Bir json dizesinden bir özellik almanın daha kolay bir yolu vardır. Bir package.jsondosyayı örnek olarak kullanarak şunu deneyin:

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

Biz kullandığınız process.envbu onların alıntı ve kodu olarak ayrıştırılmıyordur kaçan zararlı içeriklerin herhangi bir risk olmadan bir dize olarak node.js içine dosyanın içeriğini alır çünkü.


Değerleri kod olarak ayrıştırılan bir dizeye değiştirmek için dize birleştirme kullanmak, rastgele node.js kodunun çalıştırılmasına izin verir, yani Internet'ten aldığınız rastgele içerikle kullanmak son derece güvenli değildir. JavaScript'te JSON'u ayrıştırmanın güvenli / en iyi uygulama yollarının yalnızca değerlendirmemesi için bir nedeni vardır.
Charles Duffy

@CharlesDuffy takip emin değilim ama require()aslında yabancı kodu çalıştırabilirsiniz gibi JSON.parse çağrısı daha güvenli olmalıdır , JSON.parse olamaz.
Alexander Mills

Dizeniz gerçekte JSON çalışma zamanına ayrıştırıcıyı atlayacak şekilde enjekte edilmişse, bu yalnızca ve yalnızca doğru ise geçerlidir. Burada kodu güvenilir bir şekilde yapıyor görmüyorum. Bir ortam değişkeni onu çekin ve onu geçmek JSON.parse()ve evet, açık bir şekilde güvendeyiz ... fakat burada, JSON çalışma zamanı olduğunu alma (güvenilen) koduyla bant (güvenilmeyen) içeriği.
Charles Duffy

... benzer şekilde, kodunuz JSON'u dosyadan bir dize olarak okuduysanız ve bu dizeyi geçirirseniz, JSON.parse()o zaman da güvende olursunuz, ancak burada da olmaz.
Charles Duffy

1
... ahh, heck, hemen "nasıl" da olabilir. Sorun, iletilmek istediğiniz kabuk değişkenini JSON.parse()koda koymanızdır . Sen ediyoruz varsayarak alıntı sonlandırmak ve bir tırnaksız bağlamı girebilirsiniz böylece edebi komutu ters tırnak koyarak içeriği literal devam edeceğini, ancak edebi backticks dosya içeriğinde mevcut (ve dolayısıyla değişken) çünkü o, tamamen güvensiz varsayım ve nerede değerler kod olarak yürütülür.
Charles Duffy

7

Powershell artık çapraz platform olduğundan, oldukça sezgisel ve son derece basit olduğunu düşündüğüm için oraya doğru çıkacağımı düşündüm.

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

ConvertFrom-Json, JSON'u bir Powershell özel nesnesine dönüştürür, böylece bu noktadan sonraki özelliklerle kolayca çalışabilirsiniz. Örneğin, yalnızca 'id' özelliğini istiyorsanız, bunu yapmanız yeterlidir:

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

Her şeyi Bash içinden çağırmak istersen, o zaman şöyle çağırmalısın:

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

Tabii ki kıvrılmadan yapmanın saf bir Powershell yolu var, ki bu:

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

Son olarak, özel bir nesneyi JSON'a kolayca dönüştüren 'ConvertTo-Json' da var. İşte bir örnek:

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

Hangi böyle güzel JSON üretecek:

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

}

Kuşkusuz, Unix'te bir Windows kabuğu kullanmak biraz kutsaldır, ancak Powershell bazı şeylerde gerçekten iyidir ve JSON ve XML'yi ayrıştırmak bunlardan birkaçıdır. Bu, çapraz platform sürümü için GitHub sayfası https://github.com/PowerShell/PowerShell


araçlarına açık kaynak sağlamak ve açık kaynak yabancı araçlarını dahil etmek için yeni Microsoft stratejisini tanıttığınız için değerlendirildi. Dünyamız için iyi bir şey.
Alex

PowerShell'den hoşlanmıyordum, ama nesneler oldukça güzel olduğundan JSON'un işlenmesini itiraf etmeliyim.
MartinThé

6

Ayrıca xml dosyaları olan biri Xidel'e bakmak isteyebilir . Bir cli, bağımlılık ücretsiz JSONiq işlemci. (yani, xml veya json işleme için XQuery'i de destekler)

Sorudaki örnek şöyle olacaktır:

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

Veya kendi standart olmayan uzantı sözdizimimle:

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

1
Veya günümüzde daha basit: xidel -s https://api.github.com/users/lambda -e 'name'(veya -e '$json/name', veya -e '($json).name').
Reino

6

Burada cevapların hiçbirini kullanamıyorum. Kullanılabilir jq, kabuk dizisi yok, bildirim yok, grep-P yok, göz ve gözetleme yok, Python yok, Perl yok, Ruby yok, hayır - Bash yok ... Kalan cevaplar iyi çalışmıyor. JavaScript tanıdık geliyordu, ama teneke Nescaffe diyor - bu da bir hareket değil :) Var olsa bile, basit ihtiyacım için - aşırı ve yavaş olurdu.

Yine de, modemimin json formatlı cevabından birçok değişken almak benim için son derece önemlidir. Benim yönlendiriciler çok kesilmiş BusyBox ile bir soydan yapıyorum! Yalnızca awk kullanmakta sorun yok: sadece sınırlayıcıları ayarlayın ve verileri okuyun. Tek bir değişken için hepsi bu!

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

Dizilerimin olmadığını hatırlıyor musunuz? Ben bir kabuk komut dosyasında gereken 11 değişkenlere awk ayrıştırılmış veri içinde atamak zorunda kaldı. Nereye baksam, bunun imkansız bir görev olduğu söyleniyordu. Bu da sorun değil.

Benim çözümüm basit. Bu kod: 1) sorudan .json dosyasını ayrıştırır (aslında, en çok oylanan cevaptan bir çalışma veri örneği ödünç aldım) ve alıntılanan verileri seçer, artı 2) ücretsiz adlandırılmış kabuk atama awk içinden kabuk değişkenleri oluşturur değişken adları.

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"

Içinde boşluklar ile sorun yok. Benim kullanımımda, aynı komut uzun bir tek satır çıkış ayrıştırır. Eval kullanıldığından, bu çözüm yalnızca güvenilir veriler için uygundur. Verilmemiş verileri almak için uyarlamak kolaydır. Çok sayıda değişken için marjinal hız kazancı, if. Dizi eksikliği açıktır: ekstra uğraşmadan çoklu kayıt yok. Ancak dizilerin bulunduğu yerlerde, bu çözümü uyarlamak basit bir iştir.

@maikel sed cevap neredeyse çalışıyor (ama ben yorum yapamam). Güzel biçimlendirilmiş verilerim için - işe yarıyor. Burada kullanılan örnek ile çok fazla değil (eksik tırnaklar atmak). Karmaşık ve değiştirilmesi zor. Artı, 11 değişken çıkarmak için 11 çağrı yapmak zorunda sevmiyorum. Neden? 9 döngüyü ayıklayarak 100 döngüyü zamanladım: sed işlevi 48.99 saniye ve çözümüm 0.91 saniye sürdü! Adil değil? 9 değişkenli tek bir özümleme yapmak: 0.51 ve 0.02 sn.


5

Böyle bir şey deneyebilirsiniz -

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

5

Şunları kullanabilirsiniz jshon:

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

Site şöyle diyor: "İki kat hızlı, hafızanın 1 / 6'sı" ... ve sonra: "Jshon, JSON'u ayrıştırır, okur ve oluşturur. Kabuktan mümkün olduğunca kullanılabilir olacak şekilde tasarlanmıştır ve yapılan kırılgan adhoc ayrıştırıcılarının yerini alır. grep / sed / awk ve perl / python'dan yapılmış ağır tek satırlık ayrıştırıcılar. "
Roger

Bu Bash JSON ayrıştırma için önerilen çözüm olarak listelenmiştir
qodeninja

sonuç çevresindeki alıntılardan kurtulmanın en kolay yolu nedir?
gMale

4

awk ile yapmanın bir yolu

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
        }
    }
}'

4

Daha karmaşık JSON ayrıştırma için python jsonpath modülünü kullanmanızı öneririm (Stefan Goessner tarafından) -

  1. Yükle -

sudo easy_install -U Instagram Hesabındaki Resim ve Videoları jsonpath

  1. Kullanın -

Örnek file.json ( http://goessner.net/articles/JsonPath adresinden ) -

{ "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
    }
  }
}

Ayrıştır (tüm kitap başlıklarını <10 fiyatla çıkar) -

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

Çıktı verecek -

Sayings of the Century
Moby Dick

NOT: Yukarıdaki komut satırı hata denetimi içermez. hata denetimi ile tam çözüm için küçük python komut dosyası oluşturmanız ve kodu try-hariç ile sarmanız gerekir.


güzel deyim. Python'u bile bilmiyorum, ama bu güçlü bir çözüm gibi görünüyor
Sridhar Sarnobat

Bunun yerine jsonpathyüklenen küçük bir sorunla karşılaşıyordum jsonpath_rw, bu yüzden yukarıdaki işe yaramazsa deneyebileceğiniz benzer bir şey var: 1) /usr/bin/python -m pip install jsonpath-rw2) cat ~/trash/file.json | /usr/bin/python -c "from jsonpath_rw import jsonpath, parse; import sys,json; jsonpath_expr = parse('store.book[0]'); out = [match.value for match in jsonpath_expr.find(json.load(sys.stdin))]; print out;"(Birden fazla pitonla ilgili bazı sorunlar yaşadığım için python ikili yolunun tam yolunu kullandım Kurulmuş).
Sridhar Sarnobat

4

Eğer php varsa :

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

Örneğin:
json'a ülke iso kodları sağlayan bir kaynağımız var: http://country.io/iso3.json ve bunu curl ile bir kabukta kolayca görebiliriz:

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

ama çok uygun görünmüyor ve okunamaz değil, json'u daha iyi ayrıştırın ve okunabilir yapıyı görün:

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

Bu kod şöyle bir şey yazdıracaktır:

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

Eğer iç içe diziler varsa bu çıktı çok daha iyi görünür ...

Umarım bu yardımcı olur ...


4

Çok basit ama güçlü bir JSON CLI işleme aracı fx de var - https://github.com/antonmedv/fx

Bash terminalindeki JSON biçimlendirme örneği

Örnekler

Anonim işlevi kullan:

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

Anonim işlev param => ... iletmezseniz, kod otomatik olarak anonim işleve dönüştürülür. JSON'a şu anahtar kelime ile erişebilirsiniz:

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

Veya sadece nokta sözdizimini de kullanın:

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

JSON'u azaltmak için istediğiniz sayıda anonim işlev iletebilirsiniz:

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

Mevcut JSON'u forma operatörünü kullanarak güncelleyebilirsiniz:

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

Sadece düz JavaScript . Yeni sözdizimi öğrenmenize gerek yok.


GÜNCELLEME 2018-11-06

fxşimdi etkileşimli modu var ( ! )

https://github.com/antonmedv/fx


7
Kendi tasarımınızı tanıtıyorsanız, bu konuda açık olmanız gerekir. Bkz . Spam gönderici olmamak.
tripleee

4

Bu da başka bashve pythonmelez bir cevap. Bu cevabı, daha karmaşık JSON çıktısını işlemek istediğim için bash uygulamamın karmaşıklığını azalttığım için gönderdim. Ben aşağıdaki JSON nesnesi yarılıp istiyorum http://www.arcgis.com/sharing/rest/info?f=json içinde bash:

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

Aşağıdaki örnekte, kendi uygulamamı jqve unquotekaldıraçımı oluşturdum python. Python nesnesini jsonbir python sözlüğüne içe aktardığımızda , sözlükte gezinmek için python sözdizimini kullanabileceğimizi göreceksiniz . Yukarıdakiler arasında gezinmek için sözdizimi şöyledir:

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

Bash'de sihir kullanarak, datasadece python metnini veri sağına atlıyoruz ve sağlıyoruz, yani

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

Not, parametre olmadan, jq bir JSON ön hazırlayıcısı olarak işlev görür. Parametrelerle, sözlüklerde ve dizi öğelerinde gezinme dahil olmak üzere sözlükten istediğimiz her şeyi çıkarmak için python sözdizimini kullanabiliriz.

Yukarıdakileri gösteren çalışan bir örnek:

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 )"
}

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

3

Bunu, aşağıdaki gibi belirli bir değer için bir json yanıtı "ayrıştırma" yaptım:

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

Açıkçası, $ url burada twitter url olurdu ve $ var bu metin için yanıt almak için "metin" olacaktır.

Gerçekten, OP'nin tek yapmam gereken şey, aradığı belirli değişkenli çizgi için grep olduğunu düşünüyorum. Awk satırdaki ikinci öğeyi kapar ve sed ile tırnak işaretlerini kaldırırım.

Benden daha akıllı biri muhtemelen bütün düşünceyi awk veya grep ile yapabilirdi.

Şimdi, hepsini sadece sed ile yapabilirsiniz:

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

böylece, hiç garip, grep yok ... Bunu neden daha önce düşünmediğimi bilmiyorum. Hmmm ...


Aslında, sed ile yapabilirsiniz
tonybaldwin

1
grep | awk | sedVe sed | sed | sedboru hatları savurgan antipatterns vardır. Son örneğiniz kolayca yeniden yazılabilir, curl "$url" | sed '/text/!d;s/\"text\"://g;s/\"//g;s/\ //g'ancak diğerlerinin de belirttiği gibi, bu ve ilk etapta önerilmemesi gereken hataya açık ve kırılgan bir yaklaşımdır.
tripleee

Grep -oPz 'name \ ": \". *? \ "' Curloutput | sed 's / name \": / \ n / g'
Ferroao

3

JSON ayrıştırma bir kabuk komut dosyasında acı vericidir. Daha uygun bir dille, JSON özniteliklerini kabuk komut dosyası oluşturma kurallarıyla tutarlı bir şekilde ayıklayan bir araç oluşturun. Anında kabuk komut dosyası oluşturma sorununu çözmek için yeni aracınızı kullanabilir ve daha sonra gelecekteki durumlar için kitinize ekleyebilirsiniz.

Örneğin, bir araç jsonlookup düşünün ki, ben öznitelik erişiminde tanımlanan öznitelik belirteci içinde tanımlanan jsonlookup access token idöznitelik kimliğini döndürür. , muhtemelen JSON verisi olan stdin'den . Öznitelik yoksa, araç hiçbir şey döndürmez (çıkış durumu 1). Ayrıştırma başarısız olursa, durum 2'den çıkın ve stderr'a bir mesaj gönderin. Arama başarılı olursa, araç özelliğin değerini yazdırır.

JSON değerlerini ayıklamak için kesin bir unix aracı oluşturduktan sonra, kabuk komut dosyalarında kolayca kullanabilirsiniz:

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

Herhangi bir dil jsonlookup uygulaması için yapacaktır . İşte oldukça özlü bir python sürümü:

#!/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

3

Python kullanan iki astarlı. Tek bir .sh dosyası yazıyorsanız ve başka bir .py dosyasına bağımlı olmak istemiyorsanız özellikle iyi çalışır. Ayrıca boru kullanımını da artırır |. echo "{\"field\": \"value\"}"stdout'a bir json basan herhangi bir şeyle değiştirilebilir.

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

Soru bir Python çözümü aramıyordu. Yorumlara da bakın.
Andrew Barber

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.