การแยก JSON ด้วยเครื่องมือ Unix


879

ฉันพยายามที่จะแยก JSON กลับมาจากการขอขดเช่น:

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

ด้านบนจะแยก JSON ออกเป็นฟิลด์เช่น:

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

ฉันจะพิมพ์ฟิลด์เฉพาะ (แสดงโดย-v k=text) ได้อย่างไร


5
เอ่อที่ไม่ดี json แยกวิเคราะห์ btw ... สิ่งที่เกี่ยวกับตัวหนีในสตริง ... ฯลฯ มีคำตอบหลามนี้ใน SO (คำตอบ Perl แม้ ... )?
martinr

51
เมื่อใดก็ตามที่มีคนพูดว่า "ปัญหา X สามารถแก้ไขได้อย่างง่ายดายด้วยภาษาอื่น Y" นั่นคือรหัสสำหรับ"กล่องเครื่องมือของฉันมีเพียงหินสำหรับขับเล็บ ... ทำไมต้องสนใจสิ่งอื่น"
BryanH

22
@BryanH: ยกเว้นบางครั้งภาษา Y สามารถมีความพร้อมมากขึ้นในการแก้ปัญหาเฉพาะ X โดยไม่คำนึงว่าบุคคลที่แนะนำ Y รู้ว่ามีหลายภาษา
jfs

15
ค่อนข้างดึก แต่ที่นี่มันจะไป grep -Po '"'"version"'"\s*:\s*"\K([^"]*)' package.json. วิธีนี้จะช่วยแก้ปัญหางานอย่างง่ายดาย & ด้วยgrepและทำงานได้อย่างสมบูรณ์แบบสำหรับ JSON แบบง่าย ๆ สำหรับ JSON ที่ซับซ้อนคุณควรใช้เครื่องมือแยกวิเคราะห์ที่เหมาะสม
diosney

2
@auser คุณจะโอเคกับการแก้ไขการเปลี่ยนแปลง "with sed and awk" เป็น "with UNIX tools" ในชื่อหรือไม่
ชาร์ลส์ดัฟฟี่

คำตอบ:


1127

มีเครื่องมือจำนวนหนึ่งที่ออกแบบมาโดยเฉพาะเพื่อวัตถุประสงค์ในการจัดการ JSON จากบรรทัดคำสั่งและจะง่ายขึ้นและเชื่อถือได้มากกว่าการทำกับ Awk เช่นjq:

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

คุณสามารถทำได้ด้วยเครื่องมือที่ติดตั้งไว้แล้วในระบบของคุณเช่น Python ใช้jsonโมดูลและหลีกเลี่ยงการพึ่งพาพิเศษใด ๆ ในขณะที่ยังคงได้รับประโยชน์จากตัวแยกวิเคราะห์ JSON ที่เหมาะสม ข้อสันนิษฐานต่อไปนี้คุณต้องการใช้ UTF-8 ซึ่ง JSON ดั้งเดิมควรเข้ารหัสและสิ่งที่เทอร์มินัลสมัยใหม่ส่วนใหญ่ใช้เช่นกัน:

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

บันทึกทางประวัติศาสตร์

คำตอบนี้ แต่เดิมแนะนำให้ใช้jsawkซึ่งควรใช้งานได้ แต่ยุ่งยากกว่าเล็กน้อยในการใช้jqและขึ้นอยู่กับล่าม JavaScript แบบสแตนด์อโลนที่ติดตั้งซึ่งพบได้น้อยกว่าล่าม Python ดังนั้นคำตอบข้างต้นน่าจะดีกว่า:

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

คำตอบนี้เดิมทีใช้ Twitter API จากคำถาม แต่ API นั้นไม่ทำงานอีกต่อไปทำให้ยากที่จะคัดลอกตัวอย่างเพื่อทดสอบและ Twitter API ใหม่ต้องใช้คีย์ API ดังนั้นฉันจึงเปลี่ยนไปใช้ GitHub API ซึ่ง สามารถใช้งานได้ง่ายโดยไม่ต้องใช้คีย์ API คำตอบแรกสำหรับคำถามเดิมคือ:

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

7
@thrau +1 jq มันมีอยู่ในพื้นที่เก็บข้อมูลและใช้งานง่ายสุด ๆ ดังนั้นมันจึงดีกว่า jsawk ฉันทดสอบทั้งสองสามนาที jq ชนะการต่อสู้นี้
Szymon Sadło

1
โปรดทราบว่าใน Python 2 หากคุณกำลังไพพ์เอาต์พุตไปยังคำสั่งอื่นคำสั่งprintจะเข้ารหัสเป็น ASCII เสมอเพราะคุณใช้ Python ในไพพ์ แทรกPYTHONIOENCODING=<desired codec>ลงในคำสั่งเพื่อตั้งค่าการเข้ารหัสเอาต์พุตอื่นที่เหมาะสมสำหรับเทอร์มินัลของคุณ ใน Python 3 ค่าเริ่มต้นคือ UTF-8 ในกรณีนี้ (ใช้print() ฟังก์ชัน )
Martijn Pieters

1
ติดตั้ง jq บน OSX ด้วยการติดตั้งชง jq
Andy Fraley

1
curl -sเทียบเท่ากับcurl --silentในขณะที่jq -rหมายความว่าjq --raw-outputไม่มีคำพูดสตริง
Serge Stroobandt

python -c "คำขอนำเข้า; r = requests.get (' api.github.com/users/lambda');print r.json () [' ชื่อ '];" . ง่ายที่สุด!
NotTooTechy

277

ในการแยกค่าสำหรับคีย์ใดคีย์หนึ่งอย่างรวดเร็วฉันเองชอบใช้ "grep -o" ซึ่งจะคืนค่าการจับคู่ของ regex เท่านั้น ตัวอย่างเช่นหากต้องการรับฟิลด์ "ข้อความ" จากทวีตมีลักษณะดังนี้:

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

Regex นี้มีประสิทธิภาพมากกว่าที่คุณคิด ตัวอย่างเช่นมันจัดการได้ดีกับสตริงที่มีเครื่องหมายจุลภาคฝังตัวและหนีคำพูดอยู่ข้างใน ฉันคิดว่ามีงานอีกเล็กน้อยที่คุณสามารถสร้างงานที่รับรองว่าจะดึงค่าออกมาจริง ๆ ถ้าเป็นอะตอม (หากมีการซ้อนกันอยู่ regex ก็ไม่สามารถทำได้แน่นอน)

และเพื่อการทำความสะอาดเพิ่มเติม (แม้ว่าจะเป็นการรักษาสตริงเดิม) คุณสามารถใช้สิ่ง| perl -pe 's/"text"://; s/^"//; s/",$//'ต่อไปนี้: (ฉันทำสิ่งนี้เพื่อการวิเคราะห์นี้)

สำหรับผู้ที่เกลียดชังทุกคนที่ยืนยันคุณควรใช้ตัวแยกวิเคราะห์ JSON จริง - ใช่นั่นเป็นสิ่งสำคัญสำหรับความถูกต้อง แต่

  1. เพื่อทำการวิเคราะห์ที่รวดเร็วมาก ๆ เช่นการนับค่าเพื่อตรวจสอบข้อผิดพลาดในการทำความสะอาดข้อมูลหรือรับความรู้สึกทั่วไปของข้อมูล การเปิดตัวแก้ไขเพื่อเขียนสคริปต์ทำให้เสียสมาธิ
  2. grep -oคือขนาดของคำสั่งที่เร็วกว่าjsonไลบรารีมาตรฐานของ Python อย่างน้อยก็เมื่อทำสิ่งนี้เพื่อทวีต (ซึ่งแต่ละ ~ 2 KB) ฉันไม่แน่ใจว่านี่เป็นเพียงเพราะjsonช้า (ฉันควรเปรียบเทียบกับ yajl บางครั้ง); แต่โดยหลักการแล้ว regex ควรจะเร็วกว่าเนื่องจากมีสถานะ จำกัด และปรับให้เหมาะสมได้มากกว่าแทนที่จะเป็น parser ที่ต้องรองรับการเรียกซ้ำและในกรณีนี้ใช้ต้นไม้สร้าง CPU จำนวนมากสำหรับโครงสร้างที่คุณไม่สนใจ (ถ้ามีคนเขียนตัวแปลงสัญญาณสถานะอัน จำกัด ที่ทำการแยก (JSON) ที่เหมาะสม (ความลึก - จำกัด ) JSON นั่นจะยอดเยี่ยม! ในระหว่างนี้เรามี "grep -o")

ในการเขียนโค้ดที่สามารถบำรุงรักษาได้ฉันมักจะใช้ห้องสมุดการแยกวิเคราะห์จริง ฉันไม่ได้ลองjsawkแต่ถ้ามันใช้งานได้ดีมันจะพูดถึงประเด็นที่ 1

หนึ่งที่ผ่านมาแปลกประหลาดวิธีแก้ปัญหา: ฉันเขียนสคริปต์ที่ใช้ Python jsonและแยกคีย์ที่คุณต้องการลงในคอลัมน์ที่คั่นด้วยแท็บ จากนั้นฉันไปป์ผ่าน wrapper รอบ ๆawkที่อนุญาตให้เข้าถึงชื่อคอลัมน์ ในที่นี่: The json2tsv และ tsvawk สคริปต์ ดังนั้นสำหรับตัวอย่างนี้มันจะเป็น:

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

วิธีนี้ไม่ได้อยู่ # 2 มีประสิทธิภาพมากกว่าสคริปต์ Python เดียวและค่อนข้างเปราะบาง: มันบังคับให้บรรทัดใหม่และแท็บในค่าสตริงกลับสู่มาตรฐานเพื่อให้เล่นได้ดีกับมุมมองของเขตข้อมูล / ระเบียนที่คั่นด้วย awk ของโลก grep -oแต่ก็ไม่ให้คุณอยู่ในบรรทัดคำสั่งที่มีความถูกต้องมากกว่า


11
คุณลืมเกี่ยวกับค่าจำนวนเต็ม grep -Po '"text":(\d*?,|.*?[^\\]",)'
Robert

3
Robert: ถูกต้อง regex ของฉันเขียนเฉพาะค่าสตริงสำหรับฟิลด์นั้น จำนวนเต็มสามารถเพิ่มได้ตามที่คุณพูด ถ้าคุณต้องการทุกประเภทคุณต้องทำมากขึ้นเรื่อย ๆ : บูลีน, null และอาร์เรย์และวัตถุต้องทำงานมากขึ้น จำกัดความลึกเท่านั้นที่เป็นไปได้ภายใต้ regexes มาตรฐาน
เบรนแดน OConnor

9
1. jq .nameทำงานบนบรรทัดคำสั่งและไม่จำเป็นต้องมี "การเปิดโปรแกรมแก้ไขเพื่อเขียนสคริปต์" 2. ไม่สำคัญว่า regex ของคุณจะสร้างผลลัพธ์ที่ผิดได้เร็วแค่ไหน
jfs

6
และถ้าคุณต้องการค่าเพียงคุณสามารถโยนมันลงไป | grep -Po '"text":.*?[^\\]",'|awk -F':' '{print $2}'
JeffCharter

34
ดูเหมือนว่าบน OSX -Pตัวเลือกจะหายไป ผมทดสอบใน OSX 10.11.5 และเป็นgrep --version grep (BSD grep) 2.5.1-FreeBSDฉันได้รับมันทำงานกับตัวเลือก "ขยาย regex" บน OSX grep -Eo '"text":.*?[^\\]",' tweets.jsonคำสั่งจากข้างต้นจะเป็น
Jens

174

บนพื้นฐานที่คำแนะนำบางอย่างที่นี่ (โดยเฉพาะในความคิดเห็น) แนะนำให้ใช้ Python ฉันรู้สึกผิดหวังที่ไม่ได้หาตัวอย่าง

ดังนั้นนี่คือหนึ่งสายการบินเพื่อรับค่าเดียวจากข้อมูล JSON บางส่วน สมมติว่าคุณกำลังไพพ์ข้อมูลใน (จากที่อื่น) และดังนั้นควรมีประโยชน์ในบริบทการเขียนสคริปต์

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

ฉันปรับปรุงคำตอบนี้ด้านล่างเพื่อใช้ฟังก์ชั่นทุบตี: curl 'some_api' | getJsonVal 'key'
Joe Heyming

pythonpy( github.com/russell91/pythonpyมักจะเป็นทางเลือกที่ดีกว่าถึงpython -cแม้ว่ามันจะต้องมีการติดตั้งด้วย pip เพียงpy --ji -x 'x[0]["hostname"]'ไพพ์json ไปที่หากคุณไม่ต้องการใช้ built in ในการสนับสนุน json_input คุณยังสามารถรับ การนำเข้าเหล่านั้นโดยอัตโนมัติในฐานะpy 'json.loads(sys.stdin)[0]["hostname"]'
RussellStewart

2
ขอบคุณ! สำหรับการแยกวิเคราะห์ JSON ที่รวดเร็วและสกปรกฉันได้ใส่ฟังก์ชัน bash ไว้: jsonq() { python -c "import sys,json; obj=json.load(sys.stdin); print($1)"; }เพื่อให้ฉันสามารถเขียน: curl ...... | jsonq 'json.dumps([key["token"] for key in obj], indent=2)'และสิ่งที่น่ากลัวที่คล้ายกันมากกว่านี้ ... Btw obj[0]ดูเหมือนไม่จำเป็นดูเหมือนว่าจะobjใช้ได้ในกรณีเริ่มต้น (ใช่)
akavel

ขอบคุณ ฉันให้ความเคารพ JSON นี้ดีกว่าการพิมพ์เล็กน้อย:jsonq() { python -c "import sys,json; obj=json.load(sys.stdin); sys.stdout.write(json.dumps($1))"; }
Adam K Dean

4
obj[0]{ "port":5555 }ทำให้เกิดข้อผิดพลาดเมื่อแยก [0]ทำงานได้ดีหลังจากที่ถอด
CyberEd

134

ตาม MartinR และนำของ Boecko:

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

นั่นจะให้ผลลัพธ์ที่เป็นมิตรกับคุณอย่างมาก สะดวกมาก:

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

37
คุณจะแยกคีย์เฉพาะอย่างไรขณะที่ OP กำลังถาม
juan

2
คำตอบที่ดีที่สุดเพื่อให้ห่างไกล IMHO ไม่จำเป็นต้องติดตั้งอะไรอื่นใน distros | grep fieldมากที่สุดและคุณสามารถ ขอบคุณ!
Andrea Richiardi

7
ทั้งหมดนี้คือการจัดรูปแบบ JSON ถ้าฉันไม่เข้าใจผิด ไม่อนุญาตให้ผู้เรียกเลือกเขตข้อมูลเฉพาะจากผลลัพธ์เช่นเดียวกับโซลูชัน xpath หรือบางสิ่งที่ใช้ "JSON Pointer"
Cheeso

4
ฉันเพิ่งจบลงด้วยคู่ค่าคีย์ แต่ไม่ใช่ค่าในและของตัวเอง
คริส

1
jqโดยทั่วไปจะไม่ได้รับการติดตั้งในขณะที่หลามคือ นอกจากนี้เมื่อคุณอยู่ใน Python คุณก็สามารถไปได้ทั้งทางและแยกวิเคราะห์ด้วยimport json...
CpILL

125

คุณสามารถดาวน์โหลดjqไบนารีสำหรับแพลตฟอร์มของคุณและรัน ( chmod +x jq):

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

มันแยก"name"คุณลักษณะจากวัตถุ json

jqหน้าแรกบอกว่ามันเป็นเหมือนsedสำหรับข้อมูล JSON


27
เพียงบันทึกjqเป็นเครื่องมือที่น่าอัศจรรย์
hoss

2
ตกลง ฉันไม่สามารถเปรียบเทียบกับ jsawk จากคำตอบที่ยอมรับได้เนื่องจากฉันไม่ได้ใช้มัน แต่สำหรับการทดลองในท้องถิ่น (ซึ่งเป็นที่ยอมรับการติดตั้งเครื่องมือ) ฉันขอแนะนำ jq นี่เป็นตัวอย่างเล็กน้อยอย่างกว้างขวางมากขึ้นซึ่งจะมีองค์ประกอบของอาร์เรย์แต่ละคนและสังเคราะห์วัตถุ JSON ใหม่ที่มีข้อมูลที่เลือก: curl -s https://api.example.com/jobs | jq '.jobs[] | {id, o: .owner.username, dateCreated, s: .status.state}'
jbyler

2
รักสิ่งนี้. น้ำหนักเบามากและเนื่องจากมันอยู่ใน C เก่าแบบธรรมดาจึงสามารถรวบรวมได้ทุกที่
Benmj

1
หนึ่งในทางปฏิบัติมากที่สุด: มันไม่จำเป็นต้องมีห้องสมุดบุคคลที่สาม (ในขณะที่ jsawk ทำ) และง่ายต่อการติดตั้ง (OSX: ชงติดตั้ง
jq

1
นี่เป็นคำตอบที่ใช้งานได้จริงและนำไปใช้ได้ง่ายที่สุดสำหรับกรณีใช้งานของฉัน สำหรับระบบ Ubuntu (14.04) การติดตั้ง apt-get jq ง่าย ๆ ได้เพิ่มเครื่องมือในระบบของฉัน ฉันกำลังส่งเอาต์พุต JSON จากการตอบกลับ AWS CLI ไปยัง jq และใช้งานได้ดีในการแยกค่าไปยังคีย์ที่ซ้อนกันในการตอบสนอง
Brandon K

105

ใช้ Node.js

หากระบบมี ติดตั้งแล้วเป็นไปได้ที่จะใช้ค่าสถานะการ-pพิมพ์และการถอน-eสคริปต์ด้วยJSON.parseเพื่อดึงค่าใด ๆ ที่จำเป็น

ตัวอย่างง่ายๆโดยใช้สตริง JSON { "foo": "bar" }และดึงค่า "foo" ออกมา:

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

เนื่องจากเราสามารถเข้าถึงcatและยูทิลิตี้อื่น ๆ เราจึงสามารถใช้สิ่งนี้สำหรับไฟล์:

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

หรือรูปแบบอื่น ๆ เช่น URL ที่มี JSON:

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

1
ขอบคุณ! แต่ในกรณีของฉันมันใช้งานได้เฉพาะกับแฟnode -p -e 'JSON.parse(process.argv[1]).foo' '{ "foo": "bar" }'
ล็ก

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

4
นี่คือทางออกที่ฉันโปรดปราน; ใช้ภาษา (จาวาสคริปต์) เพื่อแยกโครงสร้างข้อมูลที่เป็นธรรมชาติ (JSON) ดูเหมือนว่าส่วนใหญ่ที่ถูกต้อง เช่นกัน - โหนดอาจมีอยู่แล้วในระบบและคุณไม่ต้องยุ่งกับไบนารีของ jq (ซึ่งดูเหมือนว่าจะเป็นทางเลือกที่ถูกต้อง )
Eliran Malka

นี่คือฟังก์ชั่นสคริปต์ทุบตี: # jsonv รับค่าวัตถุ json สำหรับคุณลักษณะเฉพาะ # พารามิเตอร์แรกคือเอกสาร json # พารามิเตอร์ที่สองคือคุณลักษณะที่ควรคืนค่า get_json_attribute_value () {node -pe 'JSON.parse (กระบวนการ argv [1]) [process.argv [2]] '"$ 1" "$ 2"}
Youness

6
ผลงานต่อไปนี้ของ Node.js 10:cat package.json | node -pe 'JSON.parse(fs.readFileSync(0)).version'
Ilya Boyandin

100

ใช้การสนับสนุน JSON ของ Pythonแทนการใช้ awk!

บางสิ่งเช่นนี้

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

6
ให้อภัยฉันที่พยายามหาคำตอบที่ดี ... : ฉันจะพยายามให้หนักขึ้น การเข้าข้างต้องมีมากกว่าการเขียนสคริปต์ awk เพื่อสลัดมัน!
martinr

9
ทำไมคุณถึงใช้ตัวแปร obj ในโซลูชัน oneliner นั้น มันไร้ประโยชน์และไม่ได้เก็บไว้เลยใช่ไหม? คุณเขียนน้อยใช้เป็นตัวอย่างเช่น:json.load(sys.stdin)['"key']" curl -sL httpbin.org/ip | python -c "import json,sys; print json.load(sys.stdin)['origin']"
m3nda

65

คุณถามว่าจะยิงตัวเองอย่างไรและฉันมาที่นี่เพื่อให้กระสุน:

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

คุณสามารถใช้แทนtr -d '{}' sedแต่การปล่อยพวกเขาออกไปอย่างสมบูรณ์ดูเหมือนจะมีผลตามที่ต้องการเช่นกัน

หากคุณต้องการตัดเครื่องหมายอัญประกาศด้านนอกให้ไพพ์ผลลัพธ์ของข้างบนผ่าน sed 's/\(^"\|"$\)//g'

ฉันคิดว่าคนอื่นฟังเสียงปลุกเพียงพอแล้ว ฉันจะยืนอยู่ข้างๆด้วยโทรศัพท์มือถือเพื่อเรียกรถพยาบาล ไฟไหม้เมื่อพร้อม



3
ฉันได้อ่านคำตอบทั้งหมดและอันนี้ใช้ได้อย่างสมบูรณ์แบบสำหรับฉันโดยไม่ต้องพึ่งพาใด ๆ เพิ่มเติม +1
eth0

นั่นคือสิ่งที่ฉันกำลังมองหา การแก้ไขเฉพาะ - คำสั่ง sed สำหรับการลบเครื่องหมายคำพูดไม่ทำงานสำหรับฉันฉันใช้ sed 's / "// g' แทน
AlexG

44

ใช้ Bash กับ Python

สร้างฟังก์ชั่นทุบตีในไฟล์. bash_rc ของคุณ

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

แล้วก็

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

นี่คือฟังก์ชั่นเดียวกัน แต่มีการตรวจสอบข้อผิดพลาด

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 ตรวจสอบว่ามีอย่างน้อย 1 อินพุตและ -t 0 ตรวจสอบให้แน่ใจว่าคุณเปลี่ยนเส้นทางจากไพพ์

สิ่งที่ดีเกี่ยวกับการใช้งานนี้คือคุณสามารถเข้าถึงค่า json ที่ซ้อนกันและรับ json เป็นการตอบแทน! =)

ตัวอย่าง:

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

ถ้าคุณต้องการที่จะแฟนซีจริงๆคุณสามารถพิมพ์ข้อมูล:

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

หนึ่งซับโดยไม่มีฟังก์ชั่นทุบตี:curl http://foo | python -c 'import json,sys;obj=json.load(sys.stdin);print obj["environment"][0]["name"]'
Cheeso

1
sys.stdout.write()ถ้าคุณต้องการให้มันทำงานได้ทั้ง python 2 และ 3
ต่อ Johansson

ฉันคิดว่าควรเปลี่ยนเป็น system.stdout.write (obj $ 1) ด้วยวิธีนี้คุณสามารถพูดได้: getJsonVal "['environment'] ['name']" เช่นตัวอย่างของ
@Cheeso

1
@Narek ในกรณีนั้นมันจะมีลักษณะเช่นนี้: functiongetJsonVal() { py -x "json.dumps(json.loads(x)$1, sort_keys=True, indent=4)"; }
Joe Heyming

30

TickTickเป็นตัวแยกวิเคราะห์ JSON ที่เขียนใน bash (รหัส <250 บรรทัด)

นี่คือข้อมูลสั้น ๆ ของผู้แต่งจากบทความของเขาลองจินตนาการถึงโลกที่ Bash สนับสนุน 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
ในฐานะที่เป็นคำตอบบริสุทธิ์ที่สมบูรณ์แบบเพียงคำตอบเดียวที่นี่จึงสมควรมีผู้โหวตมากขึ้น
Ed Randall

มีวิธีพิมพ์ตัวแปรคนนี้ลงในสตริง json อีกครั้งหรือไม่ นั่นจะเป็นประโยชน์อย่างยิ่ง
Thomas Fournet

1
ในที่สุดคำตอบก็ไม่แนะนำ Python หรือวิธีการร้ายอื่น ๆ ... ขอบคุณ!
Akito

21

การแยก JSON ด้วย PHP CLI

ปิดเนื้อหาได้อย่างชัดเจน แต่เนื่องจากความสำคัญเหนือกว่าคำถามนี้ยังคงไม่สมบูรณ์หากไม่มีการกล่าวถึง PHP ที่ไว้วางใจและซื่อสัตย์ของเราใช่ไหม

ใช้ JSON ตัวอย่างเดียวกัน แต่ให้กำหนดให้ตัวแปรเพื่อลดความสับสน

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

ตอนนี้สำหรับความดีของ PHP โดยใช้file_get_contentsและphp: // stdin stream wrapper

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

หรือเป็นแหลมออกโดยใช้fgetsและกระแสเปิดแล้วที่ CLI คงSTDIN

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

Njoy!


คุณสามารถใช้$argnแทนfgets(STDIN)
IcanDivideBy0

อุ๊ปส์$argnทำงานร่วมกับแฟ
ล็

21

Native Bash: ทำงานได้ดีกับแบ็กสแลช (\) และเครื่องหมายคำพูด (")

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

นี่มันเจ๋งมาก. แต่ถ้าสตริง JSON มีรหัสอีเมลมากกว่าหนึ่งคีย์ตัวแยกวิเคราะห์จะส่งออก john@doe.com "" john@doe.com
rtc11

ไม่ทำงานหากมีเส้นประในอีเมลเช่น jean-pierre@email.com
alexmngn

13

เวอร์ชันที่ใช้ Ruby และhttp://flori.github.com/json/

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

หรือสั้นกระชับ:

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

3
นี่คือรายการโปรดของฉัน;) BTW คุณสามารถย่อด้วย ruby ​​-rjson เพื่อให้ใช้ไลบรารี
lucapette

โปรดทราบว่าสุดท้าย;ไม่จำเป็นต้องใช้ใน Ruby (มันใช้สำหรับการเชื่อมงบที่ปกติจะอยู่ในบรรทัดแยกเป็นบรรทัดเดียว)
Zack Morris

11

น่าเสียดายที่คำตอบที่ได้รับการโหวตสูงสุดที่ใช้grepคืนค่าการจับคู่แบบเต็มที่ไม่ทำงานในสถานการณ์ของฉัน แต่ถ้าคุณรู้ว่ารูปแบบ JSON จะคงที่คุณสามารถใช้lookbehindและlookaheadเพื่อแยกค่าที่ต้องการ

# 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

คุณไม่เคยจริงรู้ลำดับขององค์ประกอบในพจนานุกรม JSON พวกเขาโดยไม่ได้กำหนดคำสั่ง นี่เป็นหนึ่งในเหตุผลพื้นฐานที่ทำให้การแยกวิเคราะห์ JSON ของคุณเป็นแนวทางที่ถูกต้อง
tripleee

10

หากมีคนต้องการแยกค่าจากออบเจ็กต์ JSON แบบง่ายโดยไม่ต้องใช้โครงสร้างที่ซ้อนกันคุณสามารถใช้นิพจน์ทั่วไปโดยไม่ต้องออกจาก bash

นี่คือฟังก์ชันที่ฉันกำหนดโดยใช้นิพจน์ทั่วไปของ bash ตามมาตรฐาน 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
}

Caveats: วัตถุและอาร์เรย์ไม่ได้รับการสนับสนุนเป็นค่า แต่สนับสนุนประเภทค่าอื่น ๆ ทั้งหมดที่กำหนดไว้ในมาตรฐาน นอกจากนี้คู่จะถูกจับคู่ไม่ว่าเอกสาร JSON จะลึกแค่ไหนตราบใดที่มีชื่อคีย์เหมือนกันทั้งหมด

ใช้ตัวอย่างของ 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 เราสามารถดึงค่าคุณสมบัติที่ซ้อนอยู่ด้วยฟังก์ชันนี้ได้หรือไม่?
vsbehere

8

มีวิธีที่ง่ายกว่าในการรับคุณสมบัติจากสตริง json ใช้package.jsonไฟล์เป็นตัวอย่างลองสิ่งนี้:

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

เรากำลังใช้process.envเพราะจะทำให้เนื้อหาของไฟล์นั้นอยู่ใน node.js เป็นสตริงโดยไม่มีความเสี่ยงของเนื้อหาที่เป็นอันตรายซึ่งจะทำให้ข้อความของพวกเขาถูกแยกวิเคราะห์และถูกแยกเป็นรหัส


การใช้การต่อสตริงเพื่อแทนที่ค่าเป็นสตริงแยกวิเคราะห์เป็นรหัสทำให้โค้ด node.js สามารถรันได้ซึ่งหมายความว่าไม่ปลอดภัยอย่างยิ่งที่จะใช้กับเนื้อหาแบบสุ่มที่คุณได้รับจากอินเทอร์เน็ต มีเหตุผลที่ปลอดภัย / วิธีปฏิบัติที่ดีที่สุดในการวิเคราะห์ JSON ใน JavaScript ไม่ใช่แค่ประเมิน
Charles Duffy

@CharlesDuffy ไม่แน่ใจว่าฉันติดตาม แต่การโทร JSON.parse ควรปลอดภัยกว่าเพราะrequire()สามารถเรียกใช้โค้ดต่างประเทศได้จริง JSON.parse ไม่สามารถทำได้
Alexander Mills

นั่นเป็นเรื่องจริงถ้าและเพียงอย่างเดียวถ้าสตริงของคุณถูกแทรกเข้าสู่รันไทม์ JSON ในลักษณะที่จะหลีกเลี่ยง parser ฉันไม่เห็นรหัสที่นี่ทำอย่างน่าเชื่อถือ ดึงจากตัวแปรสภาพแวดล้อมและส่งต่อไปJSON.parse()และใช่คุณปลอดภัยอย่างไม่น่าสงสัย ... แต่ที่นี่รันไทม์ JSON กำลังรับเนื้อหา (ไม่น่าเชื่อถือ) ในวงด้วยรหัส (ที่เชื่อถือได้)
Charles Duffy

... ในทำนองเดียวกันหากคุณมีรหัสของคุณอ่าน JSON จากไฟล์เป็นสตริงและส่งผ่านสตริงนั้นไปJSON.parse()คุณก็ปลอดภัยเช่นกัน แต่นั่นไม่ได้เกิดขึ้นที่นี่
Charles Duffy

1
... อ่าห่าก็อาจเข้าสู่ "อย่างไร" ในทันที ปัญหาคือว่าคุณกำลังทำหน้าที่แทนตัวแปรเปลือกที่คุณตั้งใจจะถูกส่งผ่านไปJSON.parse()ลงรหัส คุณสมมติว่าการใส่ backticks ตามตัวอักษรจะทำให้เนื้อหาเป็นตัวอักษร แต่เป็นข้อสันนิษฐานที่ไม่ปลอดภัยอย่างสมบูรณ์เพราะ backticks ตามตัวอักษรสามารถมีอยู่ในเนื้อหาไฟล์ (และทำให้ตัวแปร) และสามารถยุติการอ้างอิง ค่าจะถูกดำเนินการเป็นรหัส
Charles Duffy

7

ตอนนี้ Powershell เป็นแพลตฟอร์มข้ามฉันคิดว่าฉันจะออกไปที่นั่นเนื่องจากฉันคิดว่ามันใช้งานง่ายและค่อนข้างง่าย

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

ConvertFrom-Json แปลง JSON เป็นวัตถุที่กำหนดเอง Powershell ดังนั้นคุณสามารถทำงานกับคุณสมบัติจากจุดนั้นไปข้างหน้าได้อย่างง่ายดาย หากคุณต้องการเพียงแค่คุณสมบัติ 'id' คุณจะทำเช่นนี้:

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

หากคุณต้องการเรียกใช้สิ่งทั้งหมดจากภายใน Bash คุณจะต้องเรียกมันว่า:

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

แน่นอนมีวิธี Powershell บริสุทธิ์ที่จะทำโดยไม่ต้องขดซึ่งจะเป็น:

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

ในที่สุดก็มี 'ConvertTo-Json' ซึ่งแปลงวัตถุที่กำหนดเองเป็น JSON ได้อย่างง่ายดาย นี่คือตัวอย่าง:

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

ซึ่งจะสร้าง JSON ที่ดีเช่นนี้:

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

}

เป็นที่ยอมรับกันว่าการใช้เชลล์ของ Windows บน Unix ค่อนข้างเป็นเรื่องน่าสังเวช แต่ Powershell นั้นเก่งในบางเรื่องและการแยก JSON และ XML ออกมาเป็นคู่ นี่คือหน้า GitHub สำหรับเวอร์ชันไขว้ของแพลตฟอร์มhttps://github.com/PowerShell/PowerShell


upvoted เนื่องจากคุณกำลังส่งเสริมกลยุทธ์ใหม่ของ Microsoft ในการโอเพนซอร์ซเครื่องมือและรวมเครื่องมือโอเพนซอร์ซต่างประเทศ มันเป็นสิ่งที่ดีสำหรับโลกของเรา
อเล็กซ์

ฉันไม่ชอบ PowerShell แต่ฉันต้องยอมรับการจัดการ JSON เนื่องจากวัตถุนั้นค่อนข้างดี
MartinThé

6

ใครบางคนที่ยังมีไฟล์ XML, อาจต้องการที่จะดูที่ฉันXidel มันเป็นตัวประมวลผลJSONiq ของ cli ที่ปราศจากการพึ่งพา (เช่นมันยังรองรับ XQuery สำหรับการประมวลผล xml หรือ json)

ตัวอย่างในคำถามจะเป็น:

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

หรือด้วยไวยากรณ์ส่วนขยายที่ไม่ใช่ของฉันเอง:

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

1
หรือง่ายกว่าในปัจจุบัน: xidel -s https://api.github.com/users/lambda -e 'name'(หรือ-e '$json/name', หรือ-e '($json).name')
Reino

6

ฉันไม่สามารถใช้คำตอบใด ๆ ที่นี่ ไม่มี jq ที่พร้อมใช้งาน, ไม่มีอาร์เรย์เชลล์, ไม่ประกาศ, ไม่มี grep -P, lookbehind และ lookahead, Python, Perl ไม่มี, Ruby ไม่มี, ไม่ - แม้แต่ Bash ... คำตอบที่เหลืออยู่ใช้งานได้ดี จาวาสคริปต์ฟังดูคุ้นเคย แต่ตัวกระป๋องบอกว่า Nescaffe - มันก็ไม่เป็นไรเหมือนกัน :) แม้ว่าจะมีให้สำหรับความต้องการง่ายๆของฉัน - พวกมันจะ overkill และช้า

แต่มันเป็นสิ่งสำคัญอย่างยิ่งสำหรับฉันที่จะได้รับตัวแปรมากมายจากการตอบกลับในรูปแบบ json ของโมเด็มของฉัน ฉันกำลังทำมันในดวลจุดโทษด้วย BusyBox ลงที่เราเตอร์ของฉัน! ไม่มีปัญหาในการใช้ awk เพียงอย่างเดียว: เพียงตั้งค่าตัวคั่นและอ่านข้อมูล สำหรับตัวแปรเดียวนั่นคือทั้งหมด!

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

จำฉันได้ไหม? ฉันต้องกำหนดภายในข้อมูลที่แยกวิเคราะห์ awk ให้กับตัวแปร 11 ตัวที่ฉันต้องการในเชลล์สคริปต์ เมื่อใดก็ตามที่ฉันดูนั่นก็บอกว่าเป็นภารกิจที่เป็นไปไม่ได้ ไม่มีปัญหาด้วยเช่นกัน

ทางออกของฉันง่าย รหัสนี้จะ: 1) แยกวิเคราะห์ไฟล์. json จากคำถาม (อันที่จริงฉันยืมตัวอย่างข้อมูลการทำงานจากคำตอบ upvote ที่สุด) และเลือกข้อมูลที่ยกมาบวก 2) สร้างตัวแปรเชลล์จากภายใน awk กำหนดเชลล์ชื่อฟรี ชื่อตัวแปร

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"

ไม่มีปัญหากับช่องว่างภายใน ในการใช้งานของฉันคำสั่งเดียวกันจะแยกวิเคราะห์เอาต์พุตบรรทัดยาวเดี่ยว วิธีนี้เหมาะสำหรับข้อมูลที่เชื่อถือได้เท่านั้น มันง่ายต่อการปรับให้เข้ากับการรับข้อมูลที่ไม่มีการอ้างอิง สำหรับตัวแปรจำนวนมากการเพิ่มความเร็วเล็กน้อยสามารถทำได้หากใช้อย่างอื่น เห็นได้ชัดว่าการขาดอาร์เรย์หมายถึง: ไม่มีหลายระเบียนโดยไม่มีการเล่นซอเพิ่มเติม แต่ถ้ามีอาร์เรย์ให้ปรับใช้โซลูชันนี้เป็นงานง่าย

@maikel sed ตอบเกือบทำงาน (แต่ฉันไม่สามารถแสดงความคิดเห็นได้) สำหรับข้อมูลที่จัดรูปแบบของฉัน - มันใช้งานได้ ไม่มากกับตัวอย่างที่ใช้ที่นี่ (คำพูดที่หายไปทิ้งมันออกไป) มันซับซ้อนและยากที่จะแก้ไข นอกจากนี้ฉันไม่ต้องการโทร 11 สายเพื่อแยก 11 ตัวแปร ทำไม? ฉันจับเวลา 100 ลูปเพื่อแยกตัวแปร 9 ตัว: ฟังก์ชั่น sed ใช้เวลา 48.99 วินาทีและโซลูชันของฉันใช้เวลา 0.91 วินาที! ไม่ยุติธรรม? ทำการแยกเพียง 9 ตัวแปร: 0.51 เทียบกับ 0.02 วินาที



5

คุณสามารถใช้jshon:

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

เว็บไซต์บอกว่า: "เร็วเป็นสองเท่า, หน่วยความจำที่ 1/6" ... และจากนั้น: "Jshon วิเคราะห์, อ่านและสร้าง JSON มันถูกออกแบบมาให้ใช้งานได้มากที่สุดจากภายในเชลล์และแทนที่ตัวแยกวิเคราะห์ adhoc อันบอบบางที่ทำจาก grep / sed / awk รวมถึง parsers แบบบรรทัดเดียวที่ทำจาก perl / python "รุ่นหนา"
Roger

นี่คือรายการเป็นวิธีแก้ปัญหาที่แนะนำสำหรับการแยกวิเคราะห์ JSON ใน Bash
qodeninja

วิธีที่ง่ายที่สุดในการกำจัดเครื่องหมายคำพูดรอบผลลัพธ์คืออะไร
gMale

4

นี่เป็นวิธีหนึ่งที่คุณสามารถทำได้ด้วย 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

สำหรับการแยกวิเคราะห์ JSON ที่ซับซ้อนมากขึ้นฉันแนะนำให้ใช้โมดูล python jsonpath (โดย Stefan Goessner) -

  1. ติดตั้ง -

sudo easy_install -U jsonpath

  1. ใช้มัน -

ตัวอย่าง file.json (จาก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
    }
  }
}

แยกวิเคราะห์ (แยกชื่อหนังสือทั้งหมดด้วยราคา <10) -

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

จะส่งออก -

Sayings of the Century
Moby Dick

หมายเหตุ: บรรทัดคำสั่งด้านบนไม่รวมการตรวจสอบข้อผิดพลาด สำหรับการแก้ปัญหาอย่างเต็มรูปแบบด้วยการตรวจสอบข้อผิดพลาดคุณควรสร้างสคริปต์ไพ ธ อนขนาดเล็กและล้อมรอบโค้ดด้วยลองยกเว้น


สำนวนที่สวยงาม ฉันไม่รู้จัก Python ด้วยซ้ำ แต่ดูเหมือนว่านี่จะเป็นวิธีแก้ปัญหาที่ทรงพลัง
Sridhar Sarnobat

ฉันมีปัญหาเล็กน้อยในการติดตั้งjsonpathดังนั้นจึงติดตั้งjsonpath_rwแทนดังนั้นนี่คือสิ่งที่คล้ายกันคุณสามารถลองได้ถ้าข้างต้นใช้งานไม่ได้: 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;"(ฉันใช้เส้นทางแบบเต็มไปยังไบนารีไพ ธ อนเพราะฉันมีปัญหากับงูหลามหลายตัว ติดตั้ง)
Sridhar Sarnobat

4

หากคุณมีphp :

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

ตัวอย่างเช่น:
เรามีทรัพยากรที่ให้ json กับรหัสประเทศ iso: http://country.io/iso3.jsonและเราสามารถเห็นมันในเชลล์ด้วย curl:

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

แต่มันดูไม่สะดวกและไม่สามารถอ่านได้แจงแจงที่ดีกว่าและดูโครงสร้างที่อ่านได้:

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

รหัสนี้จะพิมพ์สิ่งที่ชอบ:

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

หากคุณมีอาร์เรย์ที่ซ้อนกันเอาต์พุตนี้จะดูดีขึ้น ...

หวังว่านี่จะเป็นประโยชน์ ...


4

นอกจากนี้ยังมีเครื่องมือประมวลผลJSON CLI ที่เรียบง่าย แต่ทรงพลังfx - https://github.com/antonmedv/fx

ตัวอย่างการจัดรูปแบบ JSON ในเทอร์มินัล Bash

ตัวอย่าง

ใช้ฟังก์ชั่นที่ไม่ระบุชื่อ:

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

หากคุณไม่ผ่านฟังก์ชันที่ไม่ระบุชื่อพารามิเตอร์ => ... โค้ดจะถูกเปลี่ยนเป็นฟังก์ชันที่ไม่ระบุชื่อโดยอัตโนมัติ และคุณสามารถเข้าถึง JSON ด้วยคำหลักนี้:

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

หรือเพียงแค่ใช้จุดซินแท็กซ์เช่นกัน:

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

คุณสามารถส่งผ่านฟังก์ชั่นนิรนามใด ๆ เพื่อลด JSON:

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

คุณสามารถอัพเดต JSON ที่มีอยู่โดยใช้ตัวดำเนินการสเปรด

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

JavaScript เพียงธรรมดา ไม่จำเป็นต้องเรียนรู้ไวยากรณ์ใหม่


อัพเดท 2018-11-06

fxตอนนี้มีโหมดโต้ตอบ ( ! )

https://github.com/antonmedv/fx


7
หากคุณกำลังส่งเสริมการสร้างของคุณเองคุณจะต้องมีความชัดเจนเกี่ยวกับมัน ดูว่าจะไม่เป็นผู้ส่งสแปมอย่างไร
tripleee

4

นี่เป็นอีกหนึ่งคำตอบbash& pythonไฮบริด ฉันโพสต์คำตอบนี้เพราะฉันต้องการประมวลผลเอาต์พุต JSON ที่ซับซ้อนมากขึ้น แต่ลดความซับซ้อนของแอปพลิเคชันทุบตีของฉัน ฉันต้องการแตกเปิดวัตถุ JSON ต่อไปนี้จากhttp://www.arcgis.com/sharing/rest/info?f=jsonในbash:

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

ในตัวอย่างต่อไปนี้ฉันสร้างการใช้งานjqและการunquoteใช้ประโยชน์ของpythonตนเอง คุณจะทราบว่าเมื่อเรานำเข้าวัตถุหลามจากjsonพจนานุกรมหลามเราสามารถใช้ไวยากรณ์หลามเพื่อสำรวจพจนานุกรม เพื่อนำทางด้านบนไวยากรณ์คือ:

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

โดยการใช้เวทย์มนตร์ใน bash เราจะละเว้นdataและใส่ข้อความ python ทางด้านขวาของข้อมูลเท่านั้น

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

หมายเหตุไม่มีพารามิเตอร์ jqทำหน้าที่เป็นตัวเลือก JSON ด้วยพารามิเตอร์เราสามารถใช้ python ไวยากรณ์เพื่อแยกสิ่งที่เราต้องการจากพจนานุกรมรวมถึงการนำย่อยและองค์ประกอบอาร์เรย์

นี่คือตัวอย่างการทำงานที่แสดงให้เห็นข้างต้น:

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

ฉันได้ทำนี้ "แจง" ตอบ json สำหรับค่าเฉพาะดังนี้

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

เห็นได้ชัดว่า $ url ที่นี่จะเป็น twitter url และ $ var จะเป็น "text" เพื่อรับการตอบกลับสำหรับ var นั้น

จริง ๆ แล้วฉันคิดว่าสิ่งเดียวที่ฉันกำลังทำ OP อยู่คือ grep สำหรับบรรทัดที่มีตัวแปรเฉพาะที่เขาค้นหา Awk คว้ารายการที่สองในบรรทัดและฉันจะตัดเครื่องหมายคำพูดออก

บางคนฉลาดกว่าที่ฉันสามารถทำได้ทั้งหมดคิดด้วย awk หรือ grep

ตอนนี้คุณสามารถทำได้ทุกอย่างด้วยเพียงแค่:

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

ดังนั้นไม่ต้องตกใจไม่มี grep ... ฉันไม่รู้ว่าทำไมฉันไม่เคยคิดถึงเรื่องนี้มาก่อน อืมม ...


จริงๆแล้วด้วยความที่คุณสามารถทำได้
tonybaldwin

1
grep | awk | sedและsed | sed | sedท่อมี antipatterns สิ้นเปลือง ตัวอย่างล่าสุดของคุณสามารถเขียนใหม่ได้อย่างง่ายดายcurl "$url" | sed '/text/!d;s/\"text\"://g;s/\"//g;s/\ //g'แต่เหมือนที่คนอื่น ๆ ชี้ให้เห็นนี่เป็นแนวทางที่ผิดพลาดและเปราะซึ่งไม่ควรแนะนำตั้งแต่แรก
tripleee

ฉันต้องใช้ grep -oPz 'name \ ": \". *? \ "' curloutput | sed 's / name \": / \ n / g'
Ferroao

3

การแยก JSON นั้นเจ็บปวดในเชลล์สคริปต์ ด้วยภาษาที่เหมาะสมยิ่งขึ้นให้สร้างเครื่องมือที่แยกแอ็ตทริบิวต์ JSON ในวิธีที่สอดคล้องกับระเบียบสคริปต์เชลล์ คุณสามารถใช้เครื่องมือใหม่ของคุณเพื่อแก้ไขปัญหาการเขียนสคริปต์เชลล์ทันทีจากนั้นเพิ่มลงในชุดอุปกรณ์ของคุณสำหรับสถานการณ์ในอนาคต

ตัวอย่างเช่นลองใช้เครื่องมือjsonlookupซึ่งถ้าฉันบอกว่าjsonlookup access token idมันจะคืนค่าIDแอตทริบิวต์ที่กำหนดไว้ภายในโทเค็นของแอตทริบิวต์ที่กำหนดไว้ภายในการเข้าถึงคุณลักษณะจาก stdin ซึ่งเป็นข้อมูล JSON ที่น่าจะเป็นไปได้ หากไม่มีแอตทริบิวต์เครื่องมือจะไม่ส่งคืนสิ่งใด (สถานะออก 1) หากการวิเคราะห์คำล้มเหลวให้ออกจากสถานะ 2 และข้อความถึง stderr หากการค้นหาสำเร็จเครื่องมือจะพิมพ์ค่าของแอททริบิวต์

การสร้างเครื่องมือ unix เพื่อจุดประสงค์ที่แม่นยำในการแตกค่า JSON คุณสามารถใช้มันในเชลล์สคริปต์ได้อย่างง่ายดาย:

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

ภาษาใดจะทำสำหรับการดำเนินงานของjsonlookup นี่เป็นเวอร์ชั่นงูหลามที่ค่อนข้างกระชับ:

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

สองซับซึ่งใช้ไพ ธ อน มันทำงานได้ดีเป็นพิเศษหากคุณเขียนไฟล์. sh ไฟล์เดียวและคุณไม่ต้องการพึ่งพาไฟล์. py ตัวอื่น |นอกจากนี้ยังใช้ประโยชน์จากการใช้งานของท่อ echo "{\"field\": \"value\"}"สามารถถูกแทนที่ด้วยสิ่งที่พิมพ์ json ไปยัง stdout

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

คำถามไม่ได้มองหาทางออกของงูหลาม ดูความคิดเห็นด้วย
Andrew Barber

3

นี่คือ usecase ที่ดีสำหรับpythonpy :

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

ยิ่งสั้นลงโมดูล python -c ที่นี่ :) ก็ดี
m3nda
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.