2016-07-14 11 views
8

Tôi đang cố gắng phân tích cú pháp đối tượng JSON trong một tập lệnh shell thành một mảng.Phân tích cú pháp JSON thành mảng trong tập lệnh shell

ví dụ: [Amanda, 25 tuổi, http://mywebsite.com]

Các JSON trông giống như:

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

Tôi không muốn sử dụng bất kỳ thư viện, nó sẽ là tốt nhất nếu tôi có thể sử dụng một biểu thức chính quy hoặc grep . Tôi đã thực hiện:

myfile.json | grep name 

Điều này mang lại cho tôi "tên": "Amanda". Tôi có thể làm điều này trong một vòng lặp cho mỗi dòng trong tập tin, và thêm nó vào một mảng nhưng tôi chỉ cần phía bên phải và không phải toàn bộ dòng.

+3

Sử dụng 'jq' cho việc này. – sjsam

+0

Hãy xem câu hỏi và hiển thị của [\ [this \]] (http://unix.stackexchange.com/questions/177843/parse-one-field-from-an-json-array-into-bash-array) chúng tôi nỗ lực một phần để giải quyết vấn đề này. – sjsam

+1

This 'cat myfile.json | tên grep | cut -d ':' -f2' có thể hữu ích. –

Trả lời

14

Nếu bạn thực sự không thể sử dụng một cú pháp JSON đúng như jq[1] , hãy thử một awk giải pháp dựa trên:

Bash 4.x:

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

Bash 3 .x:

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

Cửa hàng này tất cả các thuộc tính giá trị trong mảng Bash ${values[@]}, bạn có thể kiểm tra với
declare -p values.

Những giải pháp này có những hạn chế:

  • mỗi tài sản phải nằm trên một dòng riêng,
  • tất cả các giá trị phải được dụng dấu ngoặc kép,
  • nhúng thoát dấu ngoặc kép không được hỗ trợ.

Tất cả những hạn chế này củng cố đề xuất sử dụng trình phân tích cú pháp JSON thích hợp.


Lưu ý: Các giải pháp thay thế sau đây sử dụng Bash 4.x + readarray -t values lệnh, nhưng họ cũng làm việc với các thay thế Bash 3.x, IFS=$'\n' read -d '' -ra values.

grep + cut kết hợp: Một grep lệnh duy nhất sẽ không làm (trừ khi bạn sử dụng GNUgrep - xem dưới đây), nhưng thêm cut giúp:

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

GNUgrep: Sử dụng -P để hỗ trợ PCRE, công cụ hỗ trợ t \K để thả tất cả mọi thứ xuất hiện cho đến nay (một lựa chọn linh hoạt hơn để nhìn phía sau khẳng định) cũng như khẳng định nhìn về phía trước ((?=...)):

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

Cuối cùng, đây là một Bash tinh khiết (3 .x +) giải pháp:

Điều gì làm cho một thay thế khả thi này về hiệu suất là không tiện ích bên ngoài được gọi là trong mỗi vòng lặp; tuy nhiên, đối với các tệp nhập lớn hơn, một giải pháp dựa trên các tiện ích bên ngoài sẽ nhanh hơn nhiều.

#!/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] Đây là những gì một mạnh mẽ jq giải pháp dựa trên sẽ trông như thế (Bash 4.x):
readarray -t values < <(jq -r '.[]' myfile.json)

0

Bạn có thể sử dụng một sed lót để đạt được điều này:

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

Kết quả:

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

Nếu bạn không cần/muốn có dấu ngoặc kép thì sed sau sẽ làm gì đi với họ:

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

Kết quả:

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

Nó cũng sẽ làm việc nếu bạn có nhiều mục nhập, chẳng hạn như

$ 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 

CẬP NHẬT:

Như được chỉ ra bởi mklement0 trong nhận xét, có thể có vấn đề nếu tệp chứa khoảng trắng được nhúng, ví dụ: "name" : "Amanda lastname". Trong trường hợp này, Amandalastname cả hai sẽ được đọc vào các trường mảng riêng biệt. Để tránh điều này, bạn có thể sử dụng readarray, ví dụ,

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

này cũng sẽ chăm sóc của bất kỳ vấn đề globbing, cũng đề cập đến trong các ý kiến.

+3

Vui lòng không phân tích cú pháp đầu ra lệnh thành một mảng với 'mảng = ($ (...))' (mặc dù nó hoạt động với đầu vào mẫu): nó không hoạt động như dự định với khoảng trắng được nhúng và có thể cho kết quả trong tình cờ. – mklement0

+0

@ mklement0 Bạn có thể đưa ra một ví dụ về cách nội dung của tệp mẫu sẽ phải trông như thế nào cho một tình cờ xảy ra? – nautical

+0

Để xem cách tiếp cận của bạn làm gì để nhúng khoảng trắng, kiểm tra mảng kết quả từ 'mảng = ($ (echo 'a b'))'; để xem ảnh hưởng của việc băng ngẫu nhiên, hãy thử 'mảng = ($ (echo 'a * được sinh ra'))'. – mklement0

3

JQ là tốt, đủ để giải quyết vấn đề này

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

Vì vậy mà bạn có được một bảng và bạn có thể grep bất kỳ hàng hoặc in awk bất kỳ cột mà bạn muốn

Các vấn đề liên quan