命令列解析 JSON 資料的神奇 jq

語言: CN / TW / HK

JSON是一種廣泛使用的結構化資料格式,通常用於大多數現代API和資料服務。由於其輕量級特性和與 JavaScript 的相容性,它在 Web 應用程式中特別受歡迎。

不幸的是像Bash這樣的shell不能直接解釋和處理JSON,這意味著通過命令列使用JSON資料可能會很麻煩(又如sed,grep等工具組合進行文字操作)。

在本次講解中,我們將看看如何使用jq(瑞士軍刀般的JSON命令列處理器)解決如上的困難。

安裝jq

curl http://github.com/stedolan/jq/releases/download/jq-1.6/jq-osx-amd64 -o /usr/local/bin/jq 
// 增加可執行許可權
chmod a+x /usr/local/bin/jq
// macos 安裝辦法
brew install jq


// Linux(Ubuntu) 安裝辦法
sudo apt-get install jq

準備工作

虛擬偽資料下載:

http://gist.githubusercontent.com/hzbd/1b635c71552e2c1a9213ac2aaea98756/raw/dc6e69600532e8be09508c3505952dd189700dd2/fake_userinfo.json

jq用法解析

閒話少說,讓我們看一些例子

注意點:我們在jq命令中會使用到過濾器各種組合,請注意用於封裝所有過濾器的單引號。

1. 只打印整個JSON內容,且美化輸出:

$ cat mock.json |jq .
[
{
"_id": "6184d939a126d6f94387cb27",
"index": 0,
"guid": "a9704008-a743-49a6-8c5f-45632e564509",
"isActive": true,
"balance": "$2,380.51",
"picture": "http://placehold.it/32x32",
"age": 26,
"eyeColor": "brown",
...
]

當我們想要從API檢索資料並以清晰可讀的格式檢視響應時,能夠美化 JSON 尤其有用

2. 基本的過濾 .[], .a

// 獲取該陣列中的所有元素,而不是陣列本身
$ cat mock.json |jq '.[]'
{
"_id": "6184d939a126d6f94387cb27",
"index": 0,
"guid": "a9704008-a743-49a6-8c5f-45632e564509",
"isActive": true,
"balance": "$2,380.51",
"picture": "http://placehold.it/32x32",
"age": 26,
"eyeColor": "brown",
"name": "Fox Mcmahon",
}
...


// 要僅獲取每個item的名字欄位,只需增加.name
$ cat mock.json |jq '.[].name'
"Fox Mcmahon"
"Mcfarland Baldwin"
"Robyn Fernandez"
"Craig Mitchell"
"Tanner Figueroa"
"Grace Lott"
"Lula Miller"
"Wade Walls"
"Fitzgerald Clarke"
"Fuentes Peters"
...

3. 陣列:索引、巢狀過濾器

獲取陣列中的第一個item:

$ cat mock.json | jq '.[0]'
{
"_id": "6184d939a126d6f94387cb27",
"index": 0,
"guid": "a9704008-a743-49a6-8c5f-45632e564509",
"isActive": true,
"balance": "$2,380.51",
"picture": "http://placehold.it/32x32",
"age": 26,
"eyeColor": "brown",
"name": "Fox Mcmahon",
"gender": "male",
"company": "PHARMACON",
"email": "[email protected]",
"phone": "+1 (934) 509-2056",
"address": "192 Newkirk Placez, Grill, Northern Mariana Islands, 5877",
"about": "Non eu qui nisi in culpa ad irure. Consectetur aute in adipisicing fugiat sunt fugiat ullamco fugiat. Pariatur minim non non labore.\r\n",
"registered": "2020-02-19T02:32:34 -08:00",
"latitude": 46.538592,
"longitude": -123.07292,
"tags": [
"cupidatat",
"Lorem",
"in",
"tempor",
"in",
"consectetur",
"reprehenderit"
],
"friends": [
{
"id": 0,
"name": "Maxwell Pace"
},
{
"id": 1,
"name": "Lynda Lindsay"
},
{
"id": 2,
"name": "Alyson Hyde"
}
],
"greeting": "Hello, Fox Mcmahon! You have 7 unread messages.",
"favoriteFruit": "banana"
}

當然,你可以附加更多過濾器,用於從第一項獲取該人員的朋友欄的第一個friend的名字:

$ cat mock.json | jq '.[0].friends[0].name'
"Maxwell Pace"

4. 長度:array construction, length, pipe

$ cat mock.json | jq '.[].email' | wc -l
74

這裡你也可以使用jq自帶的length的函式來實現,首先先將email構建為一個數組,然後length計算:

$ cat mock.json | jq '[.[].email] | length'
74

5. 過濾器:select, and, contains, ==

如果你想過濾這些電子郵件的字尾怎麼辦?grep當然可以使用,但是今天我們這裡會有一個新的解決辦法:

$ cat mock.json | jq '.[].email | select(. | contains("@netur.com"))'
"[email protected]

又如我們在過濾器中再新增一個條件,從所有男性中過濾上述電子郵件:

$ cat mock.json | jq '.[] | select((.email | contains("@netur")) and .gender == "male")'
{
"_id": "6184d9397cad2b56c781d961",
"index": 28,
"guid": "40145e80-ff91-4efc-866b-dc974f923104",
"isActive": true,
"balance": "$1,947.45",
"picture": "http://placehold.it/32x32",
"age": 35,
"eyeColor": "brown",
"name": "Wright Moon",
"gender": "male",
"company": "NETUR",
"email": "[email protected]",
"phone": "+1 (923) 540-2056",
"address": "265 Franklin Street, Martinez, Iowa, 9009",
"about": "Magna ea tempor sint occaecat laborum laboris. Anim ea labore reprehenderit officia consequat laborum ut officia consequat non magna aliqua. Cupidatat ullamco sit labore id veniam mollit aute sunt nostrud sint eu sit mollit ex.\r\n",
"registered": "2021-03-08T08:55:08 -08:00",
"latitude": 22.448976,
"longitude": -119.825892,
"tags": [
"aute",
"do",
"laborum",
"cillum",
"consequat",
"do",
"pariatur"
],
"friends": [
{
"id": 0,
"name": "Regina Miranda"
},
{
"id": 1,
"name": "Blackburn Harding"
},
{
"id": 2,
"name": "Lara Dawson"
}
],
"greeting": "Hello, Wright Moon! You have 7 unread messages.",
"favoriteFruit": "banana"
}

仔細看我們發現此過濾器將返回1個item物件(不是電子郵件),因為第一個過濾器.[]返回我們使用select((.email | contains("@netur")) and .gender == "male"). 如果我們仔細看看這一點,我們將看到2個條件.gender == "male"和(.email | contains("@netur")。管道|用於應用select和contains功能。

6. 物件構造和字串插值:{}, (.a)

假設我們想要一個只包含 3 個欄位的物件陣列:first_name,last_name和email。我們需要構造一些新物件,而且語法很直觀。

$ cat mock.json | jq '.[] | {name: .name, company: .company, phone: .phone}'
{
"name": "Fox Mcmahon",
"company": "PHARMACON",
"phone": "+1 (934) 509-2056"
}
{
"name": "Mcfarland Baldwin",
"company": "XUMONK",
"phone": "+1 (836) 401-3549"
}
...

7. 分組:group_by

jq 可以做的另一件很酷的事情是分組。我們可以通過按性別分組來證明這一點。

$ cat mock.json | jq group_by(.gender)

雖然如上命令已經起作用了,但結果沒什麼可讀性 - 它是一個包含2個子陣列,其中包含分組的物件。接下來我們稍微調整一下。我們將分解 2 次(因為我們在陣列中有陣列)。

$ cat mock.json | jq 'group_by(.gender) | .[] | .[] | {name: .name, gender: .gender}'
{
"name": "Robyn Fernandez",
"gender": "female"
}
{
"name": "Grace Lott",
"gender": "female"
}
{
"name": "wuda Miller",
"gender": "male"
}
...

看起來不錯,但人員都放在一起。我們想保留2個不同的陣列:

$ cat mock.json | jq 'group_by(.gender) | .[] | [.[] | {name: .name, gender: .gender}]'
[
{
"name": "Robyn Fernandez",
"gender": "female"
},
...
]
[
{
"name": "Fox Mcmahon",
"gender": "male"
},
...
]

讓我們看看我們的資料集中有多少男人和多少女人。

$ cat mock.json | jq 'group_by(.gender) | .[] | [.[] | {name: .name, gender: .gender}] | length'
35
39

8. 切片

jq還支援陣列切片,這是另一個強大的功能。這在我們需要返回陣列的子陣列時特別有用。

$ echo '[1,2,3,4,5,6,7,8,9,10]' | jq '.[6:9]'
// 結果將是一個長度為3的新陣列,包含從索引6(包括)到索引9(不包括)的item
[
7,
8,
9
]

當然也可以這樣操作:

$ echo '[1,2,3,4,5,6,7,8,9,10]' | jq '.[:6]' | jq '.[-2:]'

9. 支援正則表示式

// 輸出名字以字母“C”開頭的所有人員的的email
$ cat mock.json|jq '.[] | select(.name|test("^C.")) | .email'
"[email protected]"
"[email protected]"
"[email protected]"
"[email protected]"
"[email protected]"
"[email protected]"

10. 從JSON中刪除敏感資訊

// 刪除所有item的balance敏感資訊欄位
$ cat mock.json|jq 'del(.[].balance)'

差不多就到這裡了,jq是一個非常強大和輕量級的工具,每個開發人員至少應該對它的工作原理有一個基本的瞭解。更多的用法建議檢視一下jq官方手冊。

參考:

http://stedolan.github.io/jq/tutorial/

http://www.json-generator.com/     // 偽資料生成

http://jqplay.org/