回想一下,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(它把+运算符应用到所有元素上,从而求出这些数字的和)[2,4,8,7]也会被传给length
jq运算符//
表达式a // b在a为 false 或 null 时输出b,否则输出a。
在给变量赋值的场景中,它非常适合用来提供默认值:
(.input.theAnswer // 42) as $answer | ...
+、-、* 就是常见的数值加法、减法和乘法运算符。在这个练习中,你将编写一些代码,帮助你按照最喜欢的烹饪书做出一份美味的千层面。
你有四个任务,全都与烹饪千层面所花的时间有关。
定义一个名为$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,
...
准备时间是指把千层面放进烤箱之前,准备好它所需要的时长。 每一层需要 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,
...