Parsing JSON dengan alat Unix


879

Saya mencoba mengurai JSON yang dikembalikan dari permintaan ikal, seperti:

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

Di atas membagi JSON menjadi bidang, misalnya:

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

Bagaimana cara mencetak bidang tertentu (dilambangkan oleh -v k=text)?


5
Erm itu tidak bagus, tetapi bagaimana dengan karakter escape dalam string ... dll ADA jawaban python untuk ini pada SO (jawaban perl bahkan ...)?
martinr

51
Setiap kali seseorang mengatakan "masalah X dapat dengan mudah diselesaikan dengan bahasa Y lainnya," itu kode untuk "kotak alat saya hanya memiliki batu untuk mengemudi paku ... mengapa repot-repot dengan hal lain?"
BryanH

22
@ BryanH: kecuali kadang-kadang bahasa Y dapat lebih diperlengkapi untuk menyelesaikan masalah tertentu X terlepas dari berapa banyak bahasa yang disarankan orang yang Anda kenal.
jfs

15
Agak terlambat, tapi ini dia. grep -Po '"'"version"'"\s*:\s*"\K([^"]*)' package.json. Ini menyelesaikan tugas dengan mudah & hanya dengan grep dan bekerja dengan sempurna untuk JSON sederhana. Untuk JSON kompleks Anda harus menggunakan parser yang tepat.
diosney

2
@auser, apakah Anda akan baik-baik saja dengan perubahan edit "dengan sed dan awk" menjadi "dengan alat UNIX" dalam judul?
Charles Duffy

Jawaban:


1127

Ada sejumlah alat yang dirancang khusus untuk tujuan memanipulasi JSON dari baris perintah, dan akan jauh lebih mudah dan lebih dapat diandalkan daripada melakukannya dengan Awk, seperti jq:

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

Anda juga dapat melakukan ini dengan alat-alat yang mungkin sudah diinstal pada sistem Anda, seperti Python menggunakan jsonmodul , dan karenanya menghindari ketergantungan tambahan, sementara masih memiliki manfaat parser JSON yang tepat. Asumsi berikut Anda ingin menggunakan UTF-8, yang mana JSON asli harus dikodekan dan apa yang digunakan sebagian besar terminal modern juga:

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

Catatan sejarah

Jawaban ini awalnya merekomendasikan jsawk , yang seharusnya masih berfungsi, tetapi sedikit lebih rumit untuk digunakan daripada jq, dan tergantung pada penerjemah mandiri JavaScript yang diinstal yang lebih jarang daripada penerjemah Python, jadi jawaban di atas mungkin lebih disukai:

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

Jawaban ini awalnya juga menggunakan API Twitter dari pertanyaan, tetapi API itu tidak lagi berfungsi, sehingga sulit untuk menyalin contoh untuk diuji, dan API Twitter baru memerlukan kunci API, jadi saya telah beralih menggunakan API GitHub yang dapat digunakan dengan mudah tanpa kunci API. Jawaban pertama untuk pertanyaan awal adalah:

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

7
@thrau +1. jq tersedia di repositori dan super mudah digunakan sehingga jauh lebih baik daripada jsawk. Saya menguji keduanya selama beberapa menit, jq memenangkan pertempuran ini
Szymon Sadło

1
Perhatikan bahwa dalam Python 2, jika Anda memipipkan output ke perintah lain maka printpernyataan itu akan selalu disandikan ke ASCII karena Anda menggunakan Python dalam sebuah pipa. Masukkan PYTHONIOENCODING=<desired codec>ke dalam perintah untuk mengatur pengkodean keluaran yang berbeda, cocok untuk terminal Anda. Dalam Python 3, standarnya adalah UTF-8 dalam kasus ini (menggunakan print() fungsi ).
Martijn Pieters

1
Instal jq di OSX dengan minuman instal jq
Andy Fraley

1
curl -ssetara dengan curl --silent, sedangkan jq -rberarti jq --raw-outputyaitu tanpa kutipan string.
Serge Stroobandt

python -c "impor permintaan; r = requests.get (' api.github.com/users/lambda');print r.json () [' name '];" . Paling sederhana!
NotTooTechy

277

Untuk mengekstrak nilai untuk kunci tertentu dengan cepat, saya pribadi suka menggunakan "grep -o", yang hanya mengembalikan kecocokan regex. Misalnya, untuk mendapatkan bidang "teks" dari tweet, sesuatu seperti:

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

Regex ini lebih kuat dari yang Anda kira; misalnya, ia berurusan dengan string yang memiliki tanda koma dan lolos dari kutipan di dalamnya. Saya pikir dengan sedikit lebih banyak pekerjaan Anda bisa membuat satu yang sebenarnya dijamin untuk mengekstraksi nilai, jika itu atomik. (Jika bersarang, maka regex tidak bisa melakukannya tentu saja.)

Dan untuk bersih lebih lanjut (meskipun menjaga melarikan diri asli string) Anda dapat menggunakan sesuatu seperti: | perl -pe 's/"text"://; s/^"//; s/",$//'. (Saya melakukan ini untuk analisis ini .)

Untuk semua pembenci yang bersikeras Anda harus menggunakan parser JSON nyata - ya, itu penting untuk kebenaran, tetapi

  1. Untuk melakukan analisis yang sangat cepat, seperti menghitung nilai untuk memeriksa bug pembersihan data atau merasakan data secara umum, menghapus sesuatu di baris perintah lebih cepat. Membuka editor untuk menulis naskah mengganggu.
  2. grep -oadalah urutan besarnya lebih cepat dari jsonpustaka standar Python , setidaknya saat melakukan ini untuk tweet (masing-masing ~ 2 KB). Saya tidak yakin apakah ini hanya karena jsonlambat (saya harus membandingkan dengan yajl kapan-kapan); tetapi pada prinsipnya, sebuah regex harus lebih cepat karena keadaannya terbatas dan jauh lebih optimal, daripada parser yang harus mendukung rekursi, dan dalam hal ini, menghabiskan banyak pohon pembangun CPU untuk struktur yang tidak Anda pedulikan. (Jika seseorang menulis transduser keadaan terbatas yang melakukan parsing JSON yang tepat (kedalaman terbatas), itu akan fantastis! Sementara itu kita memiliki "grep -o".)

Untuk menulis kode yang bisa dipelihara, saya selalu menggunakan parsing perpustakaan nyata. Saya belum mencoba jsawk , tetapi jika itu bekerja dengan baik, itu akan membahas poin # 1.

Solusi terakhir, lebih aneh,: Saya menulis sebuah skrip yang menggunakan Python jsondan mengekstrak kunci yang Anda inginkan, ke dalam kolom-kolom yang dipisahkan-tab; kemudian saya pipa melalui pembungkus di sekitar awkyang memungkinkan akses bernama ke kolom. Di sini: skrip json2tsv dan tsvawk . Jadi untuk contoh ini akan menjadi:

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

Pendekatan ini tidak membahas # 2, lebih tidak efisien daripada skrip Python tunggal, dan ini sedikit rapuh: itu memaksa normalisasi baris baru dan tab dalam nilai string, untuk bermain baik dengan bidang awk / catatan-dibatasi tampilan dunia. Tapi itu tidak membiarkan Anda tetap di baris perintah, dengan lebih banyak kebenaran daripada grep -o.


11
Anda lupa tentang nilai integer. grep -Po '"text":(\d*?,|.*?[^\\]",)'
Robert

3
Robert: Benar, regex saya hanya ditulis untuk nilai string untuk bidang itu. Bilangan bulat dapat ditambahkan seperti yang Anda katakan. Jika Anda ingin semua jenis, Anda harus melakukan lebih dan lebih lagi: booleans, null. Dan array dan objek membutuhkan kerja lebih banyak; hanya kedalaman-terbatas yang dimungkinkan, di bawah regex standar.
Brendan OConnor

9
1. jq .namebekerja pada baris perintah dan tidak memerlukan "membuka editor untuk menulis skrip". 2. Tidak masalah seberapa cepat regex Anda dapat menghasilkan hasil yang salah
jfs

6
dan jika Anda hanya menginginkan nilai-nilai itu, Anda bisa saja membuang awk padanya. | grep -Po '"text":.*?[^\\]",'|awk -F':' '{print $2}'
JeffCharter

34
Tampaknya pada OSX -Popsi hilang. Saya diuji pada OSX 10.11.5 dan grep --versionsebelumnya grep (BSD grep) 2.5.1-FreeBSD. Saya membuatnya bekerja dengan opsi "extended regex" di OSX. Perintah dari atas adalah grep -Eo '"text":.*?[^\\]",' tweets.json.
Jens

174

Atas dasar bahwa beberapa rekomendasi di sini (esp dalam komentar) menyarankan penggunaan Python, saya kecewa tidak menemukan contoh.

Jadi, inilah satu liner untuk mendapatkan nilai tunggal dari beberapa data JSON. Itu mengasumsikan bahwa Anda memipipkan data di (dari suatu tempat) dan karenanya harus berguna dalam konteks skrip.

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

Saya meningkatkan jawaban ini di bawah ini untuk menggunakan fungsi bash: curl 'some_api' | getJsonVal 'key'
Joe Heyming

pythonpy( github.com/russell91/pythonpy hampir selalu merupakan alternatif yang lebih baik python -c, walaupun itu harus diinstal dengan pip. Hanya pipa json ke py --ji -x 'x[0]["hostname"]'. Jika Anda tidak ingin menggunakan dukungan json_input bawaan, Anda masih bisa mendapatkan mereka mengimpor secara otomatis sebagaipy 'json.loads(sys.stdin)[0]["hostname"]'
RussellStewart

2
Terima kasih! Untuk penguraian JSON yang lebih cepat & kotor, saya telah membungkusnya menjadi fungsi bash: jsonq() { python -c "import sys,json; obj=json.load(sys.stdin); print($1)"; }sehingga saya dapat menulis: curl ...... | jsonq 'json.dumps([key["token"] for key in obj], indent=2)'& lebih banyak hal menyeramkan yang serupa ... Btw, obj[0]sepertinya tidak perlu, sepertinya hanya objberfungsi OK dalam kasus default (?).
aliasvel

Terima kasih. Saya telah membuat rasa hormat ini JSON sedikit lebih baik daripada mencetak:jsonq() { python -c "import sys,json; obj=json.load(sys.stdin); sys.stdout.write(json.dumps($1))"; }
Adam K Dean

4
obj[0]menyebabkan kesalahan saat parsing { "port":5555 }. Bekerja dengan baik setelah melepas [0].
CyberEd

134

Mengikuti jejak MartinR dan Boecko:

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

Itu akan memberi Anda output ramah sangat grep. Sangat mudah:

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

37
Bagaimana Anda mengekstrak kunci tertentu, seperti yang diminta OP?
juan

2
Jawaban terbaik sejauh ini, tidak perlu menginstal apa pun pada kebanyakan distro dan Anda bisa | grep field. Terima kasih!
Andrea Richiardi

7
Semua ini dilakukan adalah memformat JSON, jika saya tidak salah. Itu tidak memungkinkan penelepon untuk memilih bidang tertentu dari output, seperti solusi xpath, atau sesuatu yang didasarkan pada "JSON Pointer".
Cheeso

4
Saya hanya berakhir dengan pasangan nilai kunci, tetapi bukan nilai itu sendiri.
christopher

1
jqbiasanya tidak diinstal saat python. Juga, sekali Anda menggunakan Python, Anda bisa melanjutkan dan menguraikannya denganimport json...
CpILL

125

Anda bisa mengunduh jqbiner untuk platform Anda dan menjalankan ( chmod +x jq):

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

Ini mengekstrak "name"atribut dari objek json.

jqberanda mengatakan seperti seduntuk data JSON.


27
Sebagai catatan, jqalat yang luar biasa.
Hoss

2
Sepakat. Saya tidak dapat membandingkan dengan jsawk dari jawaban yang diterima, karena saya belum pernah menggunakannya, tetapi untuk eksperimen lokal (di mana menginstal alat dapat diterima) saya sangat merekomendasikan jq. Berikut adalah contoh yang sedikit lebih luas, yang mengambil setiap elemen dari array dan mensintesis objek JSON baru dengan data yang dipilih: curl -s https://api.example.com/jobs | jq '.jobs[] | {id, o: .owner.username, dateCreated, s: .status.state}'
jbyler

2
Suka ini. Beratnya sangat ringan, dan karena itu dalam C lama, itu dapat dikompilasi di mana saja.
Benmj

1
Yang paling praktis: tidak perlu perpustakaan pihak ketiga (sementara jsawk melakukannya) dan mudah untuk menginstal (OSX: brew install jq)
lauhub

1
Ini adalah jawaban yang paling praktis dan mudah diimplementasikan untuk kasus penggunaan saya. Untuk sistem Ubuntu (14.04), instalasi apt-get jq yang sederhana menambahkan alat ke sistem saya. Saya menyalurkan output JSON dari respons AWS CLI ke jq dan berfungsi dengan baik untuk mengekstrak nilai ke kunci tertentu yang bersarang dalam respons.
Brandon K

105

Menggunakan Node.js

Jika sistem memiliki terpasang, dimungkinkan untuk menggunakan -pcetakan dan -emengevakuasi flag skrip dengan JSON.parseuntuk menarik nilai apa pun yang diperlukan.

Contoh sederhana menggunakan string JSON { "foo": "bar" }dan mengeluarkan nilai "foo":

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

Karena kami memiliki akses ke catdan utilitas lain, kami dapat menggunakan ini untuk file:

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

Atau format lain seperti URL yang berisi JSON:

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

1
Terima kasih! tetapi dalam kasus saya ini hanya berfungsi dengan flag -enode -p -e 'JSON.parse(process.argv[1]).foo' '{ "foo": "bar" }'
Rnd_d

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

4
ini adalah solusi favorit saya; menggunakan bahasa (javascript) untuk mengurai struktur data yang alami untuk itu (JSON) sepertinya yang paling benar . juga - node mungkin sudah tersedia di sistem, dan Anda tidak perlu memotong-motong dengan binari jq (yang terlihat seperti pilihan lain yang benar ).
Eliran Malka

Ini adalah fungsi skrip bash: # jsonv mendapatkan nilai objek json untuk atribut tertentu # parameter pertama adalah dokumen json # parameter kedua adalah atribut yang nilainya harus dikembalikan get_json_attribute_value () {node -pe 'JSON.parse (proses. argv [1]) [process.argv [2]] '"$ 1" "$ 2"}
Youness

6
Berikut ini bekerja dengan Node.js 10:cat package.json | node -pe 'JSON.parse(fs.readFileSync(0)).version'
Ilya Boyandin

100

Gunakan dukungan JSON Python daripada menggunakan awk!

Sesuatu seperti ini:

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

6
Maafkan saya karena mencoba memberikan respons yang baik ...: Saya akan berusaha lebih keras. Keberpihakan membutuhkan lebih dari sekadar menulis naskah awk untuk melepaskannya!
martinr

9
Mengapa Anda menggunakan variabel obj dalam solusi oneliner itu ?. Tidak ada gunanya dan tidak disimpan sama sekali? Anda menulis kurang menggunakan json.load(sys.stdin)['"key']"sebagai contoh seperti: curl -sL httpbin.org/ip | python -c "import json,sys; print json.load(sys.stdin)['origin']".
m3nda

65

Anda telah bertanya bagaimana cara menembak diri sendiri di kaki dan saya di sini untuk memberikan amunisi:

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

Anda bisa menggunakan tr -d '{}'bukan sed. Tetapi meninggalkan mereka sepenuhnya tampaknya memiliki efek yang diinginkan juga.

Jika Anda ingin menghapus tanda kutip luar, pipa hasil dari di atas sed 's/\(^"\|"$\)//g'

Saya pikir orang lain sudah membunyikan alarm yang cukup. Saya akan berdiri dengan ponsel untuk memanggil ambulans. Api saat siap.



3
Saya sudah membaca semua jawaban dan yang ini berfungsi dengan baik untuk saya tanpa ketergantungan tambahan. +1
eth0

Itu yang saya cari. Satu-satunya koreksi - memberikan perintah sed untuk menghapus tanda kutip tidak bekerja untuk saya, saya telah menggunakan sed 's / "// g' sebagai gantinya
AlexG

44

Menggunakan Bash dengan Python

Buat fungsi bash di file .bash_rc Anda

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

Kemudian

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

Berikut adalah fungsi yang sama, tetapi dengan pemeriksaan kesalahan.

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

Di mana $ # -ne 1 memastikan setidaknya 1 input, dan -t 0 memastikan Anda mengarahkan ulang dari sebuah pipa.

Yang menyenangkan tentang implementasi ini adalah Anda dapat mengakses nilai json bersarang dan mendapatkan json sebagai imbalan! =)

Contoh:

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

Jika Anda ingin benar-benar mewah, Anda bisa mencetak datanya:

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

Satu-liner tanpa fungsi bash:curl http://foo | python -c 'import json,sys;obj=json.load(sys.stdin);print obj["environment"][0]["name"]'
Cheeso

1
sys.stdout.write()jika Anda ingin bekerja dengan kedua python 2 dan 3.
Per Johansson

Saya berpikir bahwa itu harus berubah menjadi system.stdout.write (obj $ 1). Dengan begitu Anda dapat mengatakan: getJsonVal "['environment'] ['name']", seperti contoh
@Cheeso

1
@Narek Dalam hal ini, akan terlihat seperti ini: functiongetJsonVal() { py -x "json.dumps(json.loads(x)$1, sort_keys=True, indent=4)"; }
Joe Heyming

30

TickTick adalah parser JSON yang ditulis dalam bash (<250 baris kode)

Berikut cuplikan penulis dari artikelnya, Bayangkan sebuah dunia di mana Bash mendukung JSON :

#!/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
Sebagai satu-satunya jawaban murni-bash yang kuat di sini, ini layak mendapat lebih banyak pujian.
Ed Randall

Apakah ada cara untuk mencetak variabel orang ini menjadi string json lagi? Itu akan sangat berguna
Thomas Fournet

1
Akhirnya jawaban tidak merekomendasikan Python atau metode mengerikan lainnya ... Terima kasih!
Akito

21

Parsing JSON dengan PHP CLI

Bisa dibilang off topic tetapi karena didahului memerintah pertanyaan ini tetap tidak lengkap tanpa menyebutkan PHP kami yang terpercaya dan setia, apakah saya benar?

Menggunakan contoh JSON yang sama tetapi memungkinkan menetapkannya ke variabel untuk mengurangi ketidakjelasan.

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

Sekarang untuk kebaikan PHP, menggunakan file_get_contents dan php: // stdin stream wrapper.

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

atau seperti yang ditunjukkan menggunakan gadget dan aliran yang sudah dibuka di CLI STDIN konstan .

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

nJoy!


Anda bahkan dapat menggunakan $argnsebagai gantinyafgets(STDIN)
IcanDivideBy0

Ups, $argnbekerja dengan bendera -E atau -R dan hanya jika konten JSON ada di satu baris ...
IcanDivideBy0

21

Versi Bash Asli: Juga berfungsi baik dengan garis miring terbalik (\) dan kutipan (")

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

Ini luar biasa. Tetapi jika string JSON berisi lebih dari satu kunci email, parser akan menampilkan john@doe.com "" john@doe.com
rtc11

Tidak berfungsi jika ada tanda hubung di email seperti jean-pierre@email.com
alexmngn

13

Versi yang menggunakan Ruby dan http://flori.github.com/json/

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

atau lebih ringkas:

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

3
ini adalah favorit saya;) BTW, Anda dapat menyingkatnya dengan ruby ​​-rjson untuk memerlukan perpustakaan
lucapette

Perhatikan bahwa final ;tidak diperlukan di Ruby (itu hanya digunakan untuk pernyataan gabungan yang biasanya berada pada baris terpisah menjadi satu baris).
Zack Morris

11

Sayangnya jawaban pilihan teratas yang menggunakan grepmengembalikan kecocokan penuh yang tidak berfungsi dalam skenario saya, tetapi jika Anda tahu format JSON akan tetap konstan, Anda dapat menggunakan lookbehind dan lookahead untuk mengekstrak hanya nilai yang diinginkan.

# 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

Anda tidak pernah benar - benar mengetahui urutan elemen dalam kamus JSON. Mereka, menurut definisi, tidak teratur. Ini justru salah satu alasan mendasar mengapa penggilingan JSON parser Anda sendiri adalah pendekatan yang gagal.
tripleee

10

Jika seseorang hanya ingin mengekstraksi nilai dari objek JSON sederhana tanpa perlu struktur bersarang, dimungkinkan untuk menggunakan ekspresi reguler tanpa meninggalkan bash.

Berikut adalah fungsi yang saya definisikan menggunakan bash regular expressions berdasarkan standar JSON :

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
}

Peringatan: objek dan array tidak didukung sebagai nilai, tetapi semua tipe nilai lain yang didefinisikan dalam standar didukung. Juga, pasangan akan dicocokkan tidak peduli seberapa dalam dokumen JSON itu asalkan memiliki nama kunci yang persis sama.

Menggunakan contoh OP:

$ 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 dapatkah kita mengekstraksi nilai properti bersarang dengan fungsi ini?
vsbehere

8

Ada cara yang lebih mudah untuk mendapatkan properti dari string json. Menggunakan package.jsonfile sebagai contoh, coba ini:

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

Kami menggunakan process.envkarena ini membuat konten file ke node.js sebagai string tanpa risiko konten berbahaya lolos dari kutipan mereka dan diuraikan sebagai kode.


Menggunakan penggabungan string untuk mengganti nilai menjadi string yang diuraikan sebagai kode memungkinkan kode node.js sewenang-wenang untuk dijalankan, yang berarti sangat tidak aman untuk digunakan dengan konten acak yang Anda dapatkan dari Internet. Ada alasan mengapa cara aman / praktik terbaik untuk mem-parse JSON dalam JavaScript tidak hanya mengevaluasinya.
Charles Duffy

@CharlesDuffy tidak yakin saya mengikuti tetapi panggilan JSON.parse harus lebih aman, karena require()sebenarnya dapat menjalankan kode asing, JSON.parse tidak bisa.
Alexander Mills

Itu benar jika-dan-hanya-jika string Anda benar-benar disuntikkan ke runtime JSON sedemikian rupa untuk memotong parser. Saya tidak melihat kode di sini melakukan hal itu dengan andal. Tarik dari variabel lingkungan dan serahkan ke JSON.parse()dan ya, Anda pasti aman ... tapi di sini, runtime JSON menerima konten (tidak tepercaya) di-band dengan kode (tepercaya).
Charles Duffy

... sama halnya, jika Anda meminta kode Anda membaca JSON dari file sebagai string dan meneruskannya ke string JSON.parse(), Anda juga aman, tetapi itu juga tidak terjadi di sini.
Charles Duffy

1
... ahh, sial, mungkin lebih baik masuk ke "bagaimana" segera. Masalahnya adalah Anda mengganti variabel shell, yang ingin Anda sampaikan JSON.parse(), ke dalam kode . Anda berasumsi bahwa menempatkan backticks literal akan membuat konten tetap literal, tetapi itu asumsi yang sama sekali tidak aman, karena backticks literal dapat ada dalam konten file (dan dengan demikian variabelnya), dan dengan demikian dapat mengakhiri penawaran dan memasuki konteks tanpa tanda kutip di mana nilai dieksekusi sebagai kode.
Charles Duffy

7

Sekarang Powershell adalah platform silang, saya pikir saya akan melempar keluar ke sana, karena saya merasa itu cukup intuitif dan sangat sederhana.

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

ConvertFrom-Json mengubah JSON menjadi objek kustom Powershell, sehingga Anda dapat dengan mudah bekerja dengan properti dari titik itu ke depan. Misalnya, jika Anda hanya menginginkan properti 'id', lakukan saja ini:

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

Jika Anda ingin meminta semuanya dari dalam Bash, maka Anda harus menyebutnya seperti ini:

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

Tentu saja ada cara Powershell murni untuk melakukannya tanpa keriting, yang akan menjadi:

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

Akhirnya, ada juga 'ConvertTo-Json' yang mengubah objek kustom menjadi JSON dengan mudah. Ini sebuah contoh:

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

Yang akan menghasilkan JSON bagus seperti ini:

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

}

Memang, menggunakan shell Windows di Unix agak sembrono tapi Powershell sangat pandai dalam beberapa hal, dan parsing JSON dan XML adalah beberapa di antaranya. Ini halaman GitHub untuk versi lintas platform https://github.com/PowerShell/PowerShell


terbalik karena Anda mempromosikan strategi Microsoft baru untuk membuka sumber alat mereka, dan menggabungkan alat asing sumber terbuka. Itu hal yang baik untuk dunia kita.
Alex

Saya dulu tidak suka PowerShell, tapi saya harus mengakui penanganan JSON sebagai objek cukup bagus.
MartinThé

6

Seseorang yang juga memiliki file xml, mungkin ingin melihat Xidel saya . Ini adalah prosesor JSONiq bebas cli, dependensi . (yaitu ia juga mendukung XQuery untuk pemrosesan xml atau json)

Contoh dalam pertanyaan adalah:

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

Atau dengan sintaks ekstensi non standar saya sendiri:

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

1
Atau lebih sederhana saat ini: xidel -s https://api.github.com/users/lambda -e 'name'(atau -e '$json/name', atau -e '($json).name').
Reino

6

Saya tidak dapat menggunakan jawaban apa pun di sini. Tidak ada jq yang tersedia, tidak ada susunan shell, tidak ada yang menyatakan, tidak ada grep -P, tidak ada tampilan di belakang dan lookahead, tidak ada Python, tidak Perl, tidak ada Ruby, tidak - bahkan tidak Bash ... Jawaban yang tersisa tidak bekerja dengan baik. JavaScript kedengarannya familier, tetapi kaleng itu mengatakan Nescaffe - jadi itu tidak boleh, :) Bahkan jika tersedia, untuk kebutuhan sederhana saya - mereka akan berlebihan dan lambat.

Namun, sangat penting bagi saya untuk mendapatkan banyak variabel dari json diformat dari modem saya. Saya melakukannya di sh dengan BusyBox sangat dipangkas di router saya! Tidak ada masalah menggunakan awk saja: cukup setel pembatas dan baca datanya. Untuk satu variabel, itu saja!

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

Ingat saya tidak punya array? Saya harus menetapkan dalam parsing awk data ke 11 variabel yang saya butuhkan dalam skrip shell. Ke mana pun saya melihat, itu dikatakan sebagai misi yang mustahil. Tidak masalah dengan itu juga.

Solusi saya sederhana. Kode ini akan: 1) mem-parsing file .json dari pertanyaan (sebenarnya, saya telah meminjam sampel data yang berfungsi dari jawaban yang paling banyak dipilih) dan memilih data yang dikutip, ditambah 2) membuat variabel shell dari dalam awk yang menugaskan shell bernama gratis nama variabel.

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"

Tidak ada masalah dengan kekosongan di dalam. Dalam penggunaan saya, perintah yang sama mem-parsing output baris tunggal yang panjang. Karena eval digunakan, solusi ini hanya cocok untuk data tepercaya. Sangat mudah untuk menyesuaikannya dengan pengambilan data yang tidak dikutip. Untuk sejumlah besar variabel, gain kecepatan marginal dapat dicapai dengan menggunakan else if. Kurangnya array jelas berarti: tidak ada banyak catatan tanpa mengutak-atik ekstra. Tetapi di mana array tersedia, mengadaptasi solusi ini adalah tugas yang sederhana.

@maikel sed jawaban hampir berfungsi (tapi saya tidak bisa mengomentarinya). Untuk data yang diformat dengan baik - berfungsi. Tidak terlalu banyak dengan contoh yang digunakan di sini (kutipan yang hilang membuangnya). Ini rumit dan sulit untuk dimodifikasi. Plus, saya tidak suka harus membuat 11 panggilan untuk mengekstrak 11 variabel. Mengapa? Saya menghitung 100 loop mengekstraksi 9 variabel: fungsi sed mengambil 48,99 detik dan solusi saya mengambil 0,91 detik! Tidak adil? Melakukan hanya satu ekstraksi 9 variabel: 0,51 vs 0,02 detik.


5

Anda dapat mencoba sesuatu seperti ini -

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

5

Anda bisa menggunakan jshon:

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

Situs tersebut mengatakan: "Dua kali lebih cepat, 1/6 memori" ... dan kemudian: "Jshon mem-parsing, membaca dan membuat JSON. Ini dirancang agar dapat digunakan sebanyak mungkin dari dalam shell dan menggantikan parser adhoc rapuh yang dibuat dari grep / sed / awk serta parser satu baris kelas berat yang terbuat dari perl / python. "
Roger

ini terdaftar sebagai solusi yang disarankan untuk
mem

apa cara termudah untuk menghilangkan tanda kutip di sekitar hasil?
gMale

4

inilah salah satu cara Anda dapat melakukannya dengan 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
        }
    }
}'

4

Untuk penguraian JSON yang lebih kompleks, saya sarankan menggunakan modul python jsonpath (oleh Stefan Goessner) -

  1. Pasang -

sudo easy_install -U jsonpath

  1. Gunakan -

Contoh file.json (dari 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
    }
  }
}

Parse it (ekstrak semua judul buku dengan harga <10) -

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

Akan menampilkan -

Sayings of the Century
Moby Dick

CATATAN: Baris perintah di atas tidak termasuk pengecekan kesalahan. untuk solusi lengkap dengan pengecekan kesalahan, Anda harus membuat skrip python kecil, dan bungkus kodenya dengan coba-kecuali.


idiom yang indah. Saya bahkan tidak tahu Python, tapi ini sepertinya solusi yang kuat
Sridhar Sarnobat

Saya mengalami sedikit kesulitan menginstal jsonpathjadi instal jsonpath_rwsaja, jadi di sini ada sesuatu yang serupa yang dapat Anda coba jika hal di atas tidak berhasil: 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;"(Saya menggunakan path lengkap ke python binary karena saya mengalami beberapa masalah dengan beberapa ular sanca terpasang).
Sridhar Sarnobat

4

Jika Anda memiliki php :

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

Sebagai contoh:
kami memiliki sumber daya yang menyediakan kode iso negara kepada json: http://country.io/iso3.json dan kami dapat dengan mudah melihatnya di shell dengan curl:

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

tetapi terlihat sangat tidak nyaman, dan tidak dapat dibaca, lebih baik mengurai json dan melihat struktur yang dapat dibaca:

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

Kode ini akan mencetak sesuatu seperti:

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

jika Anda memiliki array bersarang, output ini akan terlihat jauh lebih baik ...

Semoga ini bisa membantu ...


4

Ada juga alat pemrosesan JSON CLI yang sangat sederhana namun kuat fx - https://github.com/antonmedv/fx

Contoh pemformatan JSON di terminal Bash

Contohnya

Gunakan fungsi anonim:

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

Jika Anda tidak meneruskan fungsi anonim param => ..., kode akan secara otomatis diubah menjadi fungsi anonim. Dan Anda bisa mendapatkan akses ke JSON dengan kata kunci ini:

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

Atau cukup gunakan sintaks dot juga:

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

Anda dapat melewati sejumlah fungsi anonim untuk mengurangi JSON:

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

Anda dapat memperbarui JSON yang ada menggunakan operator spread:

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

JavaScript sederhana . Tidak perlu mempelajari sintaks baru.


PEMBARUAN 2018-11-06

fxsekarang memiliki mode interaktif ( ! )

https://github.com/antonmedv/fx


7
Jika Anda mempromosikan kreasi Anda sendiri, Anda harus eksplisit tentang hal itu. Lihat Bagaimana tidak menjadi spammer.
tripleee

4

Ini adalah jawaban lain bash& pythonhibrida. Saya memposting jawaban ini karena saya ingin memproses output JSON yang lebih kompleks, tetapi, mengurangi kompleksitas aplikasi bash saya. Saya ingin membuka objek JSON berikut dari http://www.arcgis.com/sharing/rest/info?f=json di bash:

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

Dalam contoh berikut, saya membuat implementasi jqdan unquotepengungkit saya sendiri python. Anda akan perhatikan bahwa setelah kami mengimpor objek python jsonke kamus python, kami dapat menggunakan sintaksis python untuk menavigasi kamus. Untuk menavigasi di atas, sintaksnya adalah:

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

Dengan menggunakan sihir di bash, kami menghilangkan datadan hanya memasok teks python ke kanan data, yaitu

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

Catatan, tanpa parameter, jq bertindak sebagai prettifier JSON. Dengan parameter, kita dapat menggunakan sintaksis python untuk mengekstraksi apa pun yang kita inginkan dari kamus termasuk menavigasi subtitle dan elemen array.

Berikut ini contoh yang menunjukkan hal di atas:

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

Saya telah melakukan ini, "mem-parsing" respons json untuk nilai tertentu, sebagai berikut:

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

Jelas, $ url di sini akan menjadi url twitter, dan $ var akan menjadi "teks" untuk mendapatkan respons untuk var itu.

Sungguh, saya pikir satu-satunya hal yang saya lakukan OP telah ditinggalkan adalah grep untuk baris dengan variabel spesifik yang dia cari. Awk meraih item kedua di telepon, dan dengan sed saya melepaskan tanda kutip.

Seseorang yang lebih pintar daripada saya mungkin bisa melakukan seluruh pemikiran dengan awk atau grep.

Sekarang, Anda bisa melakukan semuanya hanya dengan:

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

jadi, tidak awk, tidak grep ... Saya tidak tahu mengapa saya tidak memikirkan itu sebelumnya. Hmmm...


Sebenarnya, dengan sed yang bisa Anda lakukan
tonybaldwin

1
The grep | awk | seddan sed | sed | sedpipa yang antipatterns boros. Contoh terakhir Anda dapat dengan mudah ditulis ulang curl "$url" | sed '/text/!d;s/\"text\"://g;s/\"//g;s/\ //g'tetapi seperti yang telah ditunjukkan oleh orang lain, ini adalah pendekatan yang rentan kesalahan dan rapuh yang seharusnya tidak direkomendasikan.
tripleee

Saya harus menggunakan grep -oPz 'name \ ": \". *? \ "' Curloutput | sed 's / name \": / \ n / g'
Ferroao

3

Parsing JSON menyakitkan dalam skrip shell. Dengan bahasa yang lebih tepat, buat alat yang mengekstraksi atribut JSON dengan cara yang konsisten dengan konvensi skrip shell. Anda dapat menggunakan alat baru Anda untuk menyelesaikan masalah skrip shell langsung dan kemudian menambahkannya ke kit Anda untuk situasi mendatang.

Sebagai contoh, pertimbangkan alat jsonlookup sehingga jika saya mengatakan jsonlookup access token iditu akan mengembalikan atribut id yang didefinisikan dalam token atribut yang didefinisikan dalam akses atribut dari stdin, yang mungkin merupakan data JSON. Jika atribut tidak ada, alat tidak mengembalikan apa pun (status keluar 1). Jika penguraian gagal, keluar status 2 dan pesan ke stderr. Jika pencarian berhasil, alat ini mencetak nilai atribut.

Setelah membuat alat unix untuk tujuan yang tepat dalam mengekstraksi nilai JSON Anda dapat dengan mudah menggunakannya dalam skrip shell:

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

Bahasa apa pun akan dilakukan untuk implementasi jsonlookup . Berikut ini adalah versi python yang cukup ringkas:

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

Garis dua yang menggunakan python. Ini bekerja sangat baik jika Anda sedang menulis file .sh tunggal dan Anda tidak ingin bergantung pada file .py lainnya. Ini juga memanfaatkan penggunaan pipa |. echo "{\"field\": \"value\"}"dapat digantikan oleh apapun yang mencetak json ke stdout.

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

Pertanyaannya adalah tidak mencari solusi Python. Lihat komentarnya juga.
Andrew Barber

3

Ini adalah usecase yang bagus untuk pythonpy :

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

Bahkan lebih pendek, modul python -c di sini :) bagus.
m3nda
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.