jqのプログラムは_式のパイプライン_であることを思い出しましょう。
変数の代入は次のルールに従います。_変数の代入_とは、次のような形をした式です。
... | expr as $varname | ...
恒等式.と同じように、_変数の代入_の出力は入力と同じです。
変数の値を設定することは副作用です。
次の例は、代入のあとの.が最初の入力と同じであることを示しています。
$ jq -c -n '42 | (. * 2) as $double | [., $double]'
[42,84]
変数のスコープは「レキシカル」です。その変数は、パイプラインの残りの部分で有効になります。 関数の中で定義された変数(関数については後で扱います)は、その関数に「ローカル」です。
変数名は$で始まる必要があります。
変数名の残りの部分は識別子です。識別子は、文字またはアンダースコアで始まり、そのあとに文字、数字、アンダースコアが続きます。
マニュアルにはこう書かれています。
単一の
as式で複数の変数を宣言できます。その際、_入力の構造に一致する_パターンを指定します(これは「分割代入」として知られています)。
例:
[1, 2, 3] as [$a, $b, $c]
| [$a, $b, $c] # => [1, 2, 3]
{"foo": "bar", "len": [10, 20]} as {foo: $f, len: [$h, $w]}
| [$f, $h, $w] # => ["bar", 10, 20]
変数名を_オブジェクトのキー_に一致させると、省略して書くことができます。
{"x": 4, "y": 7} as {$x, $y}
| [$x, $y] # => [4, 7]
{"foo": "bar", "len": [10, 20]} as {$foo, $len}
| [$foo, $len] # => ["bar", [10, 20]]
同じ省略のしかたは、_オブジェクトの構築_でも逆向きに使えます。
4 as $x | 7 as $y | {$x, $y} # => {"x":4, "y": 7}
上の例で見たように、変数は定数を保存するのに便利です。 コードの中に「マジックナンバー」が現れるより、定数には名前を付けたほうがよいでしょう。
変数を省略したほうが読みやすくなることもよくあります。
数値のリストの平均を計算することを考えてみましょう。
[2, 4, 8, 7]
| add as $sum
| length as $len
| $sum / $len
次の書き方と比べてみましょう。
[2, 4, 8, 7] | add / length
これは、jqの最も強力な特徴のひとつを浮き彫りにします。フィルターへの入力は、_すべての_部分式に渡されるのです。
[2,4,8,7]がaddに渡されます(addは、すべての要素に+演算子を適用して数値を合計します)[2,4,8,7]はlengthにも渡されますjqの演算子//
式a // bは、aがfalseまたはnullならbを出力し、そうでなければaを出力します。
変数を設定する場面では、これはデフォルト値を用意するのにとても便利です。
(.input.theAnswer // 42) as $answer | ...
+、-、*は、通常の数値の加算、減算、乗算の演算子です。この演習では、お気に入りの料理本の絶品ラザニア作りに役立つコードを書いていきます。
タスクは4つあり、どれもラザニアを作るのにかかる時間に関するものです。
$expected_minutes_in_ovenという名前の定数を定義します。
料理本によると、想定されるオーブン時間は40分です。
$ jq -n -f lasagna.jq
{
"expected_minutes_in_oven": 40,
...
この値は、ラザニアをオーブンに入れてからの実際の経過時間(分)を入力として受け取り、前のタスクで定義した想定オーブン時間(分)をもとに、ラザニアをあと何分オーブンに入れておく必要があるかを返します。
$ echo '{"actual_minutes_in_oven": 18}' | jq -f lasagna.jq
{
"expected_minutes_in_oven": 40,
"remaining_minutes_in_oven": 22,
...
準備時間とは、ラザニアをオーブンに入れる前に準備を整えるのにかかる時間です。 1層の準備には2分かかります。
$ echo '{"number_of_layers": 3}' | jq -f lasagna.jq
{
...
"preparation_time": 6,
...
ラザニア作りに合計何分かけたかを計算します。 これは、準備時間(分)と、現時点でラザニアがオーブンに入っている時間(分)を足したものです。
$ echo '{"actual_minutes_in_oven": 10, "number_of_layers": 2}' | jq -f lasagna.jq
{
...
"total_time": 14,
...