jq 的運作方式,是把傳入的 JSON 資料通過一個_單一運算式_(寫成_過濾器管線_)來得到想要的轉換結果。
jq命令列選項快速入門jq語言是由jq_程式_所實作的。
這個程式提供了幾個方便的命令列選項,用來控制輸入如何被取用,以及輸出如何呈現。
在底下的範例中,你會遇到:
-n或--null-input
一般來說,jq程式會拿到一個檔案來讀取,或者你把資料送到它的輸入。
--null-input選項讓你可以不需要任何輸入就產生 JSON 資料。
-c或--compact-output
jq預設會美化輸出。
把資料排得漂漂亮亮再閱讀,對人類來說非常有用。
但機器不需要這樣:--compact-output選項會移除排版用的空白,把產生的 JSON 縮到最小。
-f filename或--from-file filename
從filename讀取jq程式,而不是在命令列上直接提供。
sed和awk也基於同樣的目的使用-f選項。
你會在練習題的測試腳本裡看到這種用法。
關於所有選項的詳細說明,請參閱手冊。
過濾器也稱為運算式。
一個_過濾器_接受輸入並產生輸出。
就像你在 Unix shell 裡工作的方式,你可以用管線|把_過濾器_串接起來,將一個_過濾器_的輸出接到另一個_過濾器_的輸入。
.
這是最簡單的_過濾器_。
它就只是把輸入原封不動地傳到輸出。
例如,jq預設會美化輸出,所以把 JSON 送進.過濾器,就能免費得到排得漂漂亮亮的輸出!
$ echo '[1, 2, 3]' | jq '.'
[
1,
2,
3
]
這裡會快速介紹如何使用陣列。 之後我們會更詳細地探討這個主題。
_陣列_元素用方括號存取,索引從 0 開始。
$ echo '[10, 20, 30]' | jq '.[1]'
20
一個_過濾器_可以把運算式包在[和]裡來建立_陣列_:
搭配一組已知的元素:
jq -n '[1, 2, 3]'
把一串元素收集起來:例如
range是一個會輸出數字串流的函式
$ jq -n 'range(10; 70; 15)'
10
25
40
55
使用[]可以把運算式的結果收集成一個陣列
$ jq -c -n '[range(10; 70; 15)]'
[10,25,40,55]
_逗號_不只是分隔_陣列_元素的語法。 逗號是一個用來串接串流的運算子。
例如,[1, 2, 3]是一個_過濾器_,它用_陣列_建構子[]把串接三個運算式1、2和3的結果收集起來。
你注意到上面range(10; 70; 15)裡的分號了嗎?
因為_逗號_在jq語言裡有特定的用途,所以接受多個引數的函式會用分號來分隔引數。
快速介紹一下物件。
和許多程式語言類似,用點來存取_物件_的屬性
$ echo '{"foo": {"bar": "qux"}}' | jq '.foo.bar'
"qux"
方括號也可以用在_物件_上,但這時字串字面值就需要加上引號。 這是處理含有空白的鍵的其中一種方法。
$ echo '{"foo bar": "qux"}' | jq '.["foo bar"]'
"qux"
你可以用{}和key: value配對來建構_物件_。
「簡單」的字串_鍵_前後不需要加引號。
jq -n '{question: (6 * 9), answer: 42}'
會輸出
{
"question": 54,
"answer": 42
}
要把_鍵_當成_運算式_處理,就必須用括號把它包起來(下面這個也會輸出和上面相同的結果)。
echo '[["question", "answer"], [54, 42]]' \
| jq '{(.[0][0]): .[1][0], (.[0][1]): .[1][1]}'
從一個大物件裡取出部分鍵是很常見的需求。
例如,要從下面這個物件中取出id和name:
{
"id": 101,
"name": "alpha widget",
"specifications": {...}
}
我們可以寫成
{id: .id, name: .name}
但這種需求太常見了,所以有簡寫語法:
{id, name}
例如,假設file.json的內容是
{
"key1": "value1",
"key2": [5, 15, 25]
}
我們來計算 key2 這個_陣列_的長度:
$ jq '.key2 | length' file.json
3
我們把.key2運算式的輸出_用管線傳送_給length當作輸入,它不意外地會輸出陣列裡的元素數量。
這是jq需要花點時間習慣的一個地方。
大多數(但不是全部)函式的行為像_過濾器_,資料是送進過濾器的輸入,而不是當成引數傳入。
在這個例子裡,輸入的 JSON 資料會被忽略,對輸出沒有任何影響:
$ echo '{"answer": 42}' | jq '6 * 9'
54
一個_過濾器_可以輸出多個值。
例如,.[]_過濾器_會把_陣列_的每個元素當成獨立的值輸出:
$ jq -n -c '[1, 2, 3]'
[1,2,3]
$ jq -n -c '[1, 2, 3] | .[]'
1
2
3
把這樣的_過濾器_用管線接到另一個_過濾器_,會**針對每個值**執行第二個_過濾器_:
$ jq -n -c '[1, 2, 3] | .[] | . * 2'
2
4
6
這就像是隱含的疊代。
一旦你懂了這個技巧,就會發現非常強大的jq_過濾器_可以寫得非常精簡。
括號用來把子運算式組成一群,以強制運算的順序,就像其他語言一樣。
在jq裡,需要用到括號的時機有時會讓人覺得有點意外。
例如,假設我們想建立一個有 2 個元素的_陣列_:9 的平方根,以及 e 的 1 次方。
這兩個獨立的運算式是9 | sqrt和1 | exp。
我們預期輸出會是_陣列_[3, 2.7...]
$ jq -n '[ 9|sqrt, 1|exp ]'
[
20.085536923187668,
2.718281828459045
]
為什麼我們沒有得到預期的結果?jq把它解讀成這樣:
[ ((9|sqrt), 1) | exp ]
jq建立了一個有兩個元素(3和1)的串流,這兩個元素都會被送進exp。
我們必須確保exp只會把一個數字當作輸入。
換句話說,我們必須強制管線比逗號先求值。
$ jq -n '[ 9|sqrt, (1|exp) ]'
[
3,
2.718281828459045
]
摘錄自手冊
jq 支援的資料型態和 JSON 相同:數字、字串、布林值、陣列、物件(用 JSON 的說法,就是只有字串鍵的雜湊),以及 "null"。
之後的練習會讓你更了解這些。
空白在jq裡不具意義。
你可以自由使用空格、定位點或換行來編排你的程式碼。
我們並不知道有任何現成的jq風格指南。
jq裡的值是_不可變的_。
修改值的過濾器會輸出一個新的值。
這表示jq沒有全域變數,你需要習慣把狀態從一個過濾器傳到另一個過濾器。
false和null這兩個值會被視為 false。其他任何值
(包括數字零,以及空字串、空陣列、空物件)都是 true。
我們不深入探討(函式會是另一個練習的主題),這裡先列出幾個好用的內建函式:
以陣列作為輸入,輸出陣列中的元素數量。
$ jq -n '[10, 20, 30, 40] | length'
4
這個運算子的行為取決於運算元的型別: 它會相加數字、 串接字串、 附加陣列、 合併物件。
$ jq -c -n '
3 + 4,
"foo" + "bar",
["a", "b"] + ["c"],
{"m": 10} + {"n": 20}
'
7
"foobar"
["a","b","c"]
{"m":10,"n":20}
add是一個接受陣列、並依照+的規則把所有元素加總後回傳一個結果的函式。
[1, 2, 3] | add會輸出6。
以陣列作為輸入、過濾器作為引數,輸出把過濾器套用到每個元素後得到的陣列
$ jq -c -n '[10, 20, 30, 40] | map(. / 5)'
[2,4,6,8]
給定_某個_輸入和一個過濾器作為引數:
null值,而是真的沒有任何輸出)例如,給定一些數字,選出可以被 3 整除的:
$ jq -n 'range(10) | select(. % 3 == 0)'
0
3
6
9
回想一下,range會輸出數字的_串流_。
每個數字都會呼叫一次select。
只有「通過」運算式的數字才會被輸出。
你常常需要選出陣列中的元素。 這裡有幾種做法。
以["Anne", "Bob", "Cathy", "Dave"]為輸入,選出長度為 4 的名字。
把map和select一起使用
map(select(length == 4))
把陣列展開成一個個元素,對那個串流做select,再收集結果
[ .[] | select(length == 4) ]
註解以#字元開頭,一直到該行的結尾。