2016-07-14 10 views
8

मैं एक जेएसओएन ऑब्जेक्ट को एक सरणी स्क्रिप्ट के भीतर एक सरणी में पार्स करने का प्रयास कर रहा हूं।पार्स JSON एक शेल स्क्रिप्ट में सरणी

जैसे: [अमांडा, 25, http://mywebsite.com]

JSON लगता है:

{ 
    "name"  : "Amanda", 
    "age"  : "25", 
    "websiteurl" : "http://mywebsite.com" 
} 

मैं नहीं चाहता कि किसी भी पुस्तकालयों का उपयोग करने के लिए, यह सबसे अच्छा होगा अगर मैं एक नियमित अभिव्यक्ति या ग्रेप इस्तेमाल कर सकते हैं चाहता हूँ । मैंने किया है:

myfile.json | grep name 

यह मुझे "नाम" देता है: "अमांडा"। मैं इसे फ़ाइल में प्रत्येक पंक्ति के लिए एक लूप में कर सकता हूं, और इसे सरणी में जोड़ सकता हूं लेकिन मुझे केवल सही तरफ की आवश्यकता है, न कि पूरी लाइन।

+3

उपयोग grep कर सकते हैं। – sjsam

+0

[\ [यह \]] पर एक नज़र डालें [http://unix.stackexchange.com/questions/177843/parse-one-field-from-an-json-array-into-bash-array) प्रश्न और शो इसे हल करने के लिए हम आपके हिस्से पर कुछ प्रयास करते हैं। – sjsam

+1

यह 'बिल्ली myfile.json | grep नाम | कट-डी ':' -एफ 2' मदद कर सकता है। –

उत्तर

14

तुम सच में एक उचित JSON पार्सर उपयोग नहीं कर सकते जैसे jq[1] , एक awk आधारित समाधान कोशिश:

बैश 4.x:

readarray -t values < <(awk -F\" 'NF>=3 {print $4}' myfile.json) 

बैश 3 .x:

IFS=$'\n' read -d '' -ra values < <(awk -F\" 'NF>=3 {print $4}' myfile.json) 

यह स्टोर सभी संपत्ति मूल्य बैश सरणी ${values[@]} में, जिसे आप
declare -p values के साथ देख सकते हैं।

  • प्रत्येक संपत्ति अपनी पंक्ति में होना चाहिए,
  • सभी मूल्यों डबल-कोटेड होना चाहिए,
  • एम्बेडेड भाग निकले दोहरे उद्धरण चिह्नों समर्थित नहीं हैं:

    ये समाधान सीमाएं हैं।

ये सभी सीमाएं उचित JSON पार्सर का उपयोग करने की सिफारिश को मजबूत करती हैं।


नोट: वैकल्पिक समाधान निम्नलिखित बैश 4.x + readarray -t values आदेश का उपयोग करें, लेकिन वे भी बैश 3.x विकल्प, IFS=$'\n' read -d '' -ra values साथ काम करते हैं।

grep + cut संयोजन: एक grep आदेश से काम नहीं चलेगा (जब तक आप जीएनयूgrep का उपयोग करें - नीचे देखें), लेकिन cut जोड़ने में मदद करता है:

readarray -t values < <(grep '"' myfile.json | cut -d '"' -f4) 

जीएनयूgrep: पीसीआरई का समर्थन करने के लिए -P का उपयोग करना, जो समर्थक

readarray -t values < <(grep -Po ':\s*"\K.+(?="\s*,?\s*$)' myfile.json) 

अंत में, यहाँ एक शुद्ध बैश है (3: टी \K सब कुछ अब तक का मिलान नहीं हुआ (एक नज़र-पीछे दावा करने के लिए एक और अधिक लचीला विकल्प) के साथ ही लुक-आगे दावे ((?=...)) ड्रॉप करने .x +) समाधान:

क्या प्रदर्शन के संदर्भ में इस एक व्यवहार्य विकल्प है कि बनाता है कोई बाहरी उपयोगिताओं प्रत्येक पाश यात्रा में कहा जाता है है, हालांकि, बड़ी इनपुट फ़ाइलों के लिए, बाहरी उपयोगिताओं के आधार पर एक समाधान बहुत तेज होगा।

#!/usr/bin/env bash 

declare -a values # declare the array                                         

# Read each line and use regex parsing (with Bash's `=~` operator) 
# to extract the value. 
while read -r line; do 
    # Extract the value from between the double quotes 
    # and add it to the array. 
    [[ $line =~ :[[:blank:]]+\"(.*)\" ]] && values+=("${BASH_REMATCH[1]}") 
done < myfile.json                                   

declare -p values # print the array 

[1] यहाँ एक मजबूत jq आधारित समाधान (बैश 4.x) इस तरह दिखाई देगा:
readarray -t values < <(jq -r '.[]' myfile.json)

0

आप एक sed एक लाइनर का उपयोग कर सकते इसे प्राप्त करने के लिए:

array=($(sed -n "/{/,/}/{s/[^:]*:[[:blank:]]*//p;}" json)) 

परिणाम:

$ echo ${array[@]} 
"Amanda" "25" "http://mywebsite.com" 

आप की जरूरत नहीं है/उद्धरण चिह्नों उसके बाद निम्न एसईडी उन लोगों के साथ दूर करना होगा चाहते हैं:

array=($(sed -n '/{/,/}/{s/[^:]*:[^"]*"\([^"]*\).*/\1/p;}' json)) 

परिणाम:

$ echo ${array[@]} 
Amanda 25 http://mywebsite.com 

यह भी काम करेंगे यदि आपके पास एकाधिक प्रविष्टियां हैं, जैसे

$ cat json 
{ 
    "name"  : "Amanda" 
    "age"  : "25" 
    "websiteurl" : "http://mywebsite.com" 
} 

{ 
    "name"  : "samantha" 
    "age"  : "31" 
    "websiteurl" : "http://anotherwebsite.org" 
} 

$ echo ${array[@]} 
Amanda 25 http://mywebsite.com samantha 31 http://anotherwebsite.org 

अद्यतन:

के रूप में टिप्पणी में mklement0 से कहा, वहाँ कोई समस्या हो सकती है, तो फ़ाइल एम्बेडेड खाली स्थान के, उदा होता है, "name" : "Amanda lastname"। इस मामले में Amanda और lastname दोनों को अलग-अलग सरणी फ़ील्ड में पढ़ा जाएगा। यह आप readarray उपयोग कर सकते हैं, उदाहरण के लिए बचने के लिए,

readarray -t array < <(sed -n '/{/,/}/{s/[^:]*:[^"]*"\([^"]*\).*/\1/p;}' json2) 

यह भी किसी ग्लोबिंग मुद्दों, भी टिप्पणी में उल्लेख की देखभाल करेंगे।

+3

कृपया सरणी आउटपुट को सरणी = ($ (...)) के साथ सरणी में पार्स न करें (भले ही यह नमूना इनपुट के साथ काम करने के लिए होता है): यह एम्बेडेड व्हाइटस्पेस के साथ काम नहीं करता है और परिणाम हो सकता है आकस्मिक globbing में। – mklement0

+0

@ mklement0 क्या आप उदाहरण दे सकते हैं कि नमूना फ़ाइल की सामग्री को आकस्मिक ग्लोबिंग के लिए कैसा दिखना होगा? – nautical

+0

यह देखने के लिए कि आपका दृष्टिकोण एम्बेडेड व्हाइटस्पेस के लिए क्या करता है, उस सरणी की जांच करें जो 'array = ($ (echo' a b ') के परिणाम है;'; आकस्मिक ग्लोबिंग के प्रभाव देखने के लिए, 'array = ($ (echo' a * जन्म हुआ है ') आज़माएं। – mklement0

3

JQ काफी अच्छा इस समस्या

paste -s <(jq '.files[].name' YourJsonString) <(jq '.files[].age' YourJsonString) <(jq '.files[].websiteurl' YourJsonString) 

हल करने के लिए तो यह है कि आप एक मेज मिलता है और आप किसी भी पंक्तियों या awk प्रिंट किसी भी वांछित स्तंभ `इस के लिए jq`

संबंधित मुद्दे