动作
在一个 模式–动作 语句中, 模式决定动作什么时候执行。有时候动作会非常简单: 一条单独的打印语句或赋值语句. 在有些时候, 动作有可能是多条语句, 语句之间用换行符或分号分开。
表达式
表达式是最简单的语句, 大多数其他语句都是由不同类型的表达式组合而成。
初等表达式是最原始的构造块: 它们包括常量, 变量, 数组引用, 函数调用, 以及各种内建变量, 例如字段的名字。
常量
Awk 中只有两种类型的常量: 字符串与数值. 将一个字符序列用一对双
引号包围起来就创建了一个字符串常量,如 “hello”。
一个数值常量可以是一个整数, 就像 1127, 或十进制小数, 3.14等。
变量
表达式可以包含若干种类型的变量: 用户定义的, 内建的, 或字段. 用户定义的变量名字由数字, 字母与下划线构成, 但是名字不能以数字开始. 所有内建变量的名字都只由大写字母组成。
每一个变量都有一个值, 这个值可以是字符串或数值, 或两者都是. 因为变量的类型不需要事先声明, 所以 awk 需要根据上下文环境推断出变量的类型。
内建变量
内建变量属于 awk 本身就内置的变量,可以直接拿来使用。这些变量可以用在所有的表达式中, 而且可以被用户重置。每当有一个新的文件被读取, FILENAME 就会被重新赋值. 每当有一个新的记录被读进来, FNR,NF, NR 就会被重新赋值。
字段变量
当前输入行的字段从 $1, $2, 一直到 $NF; $0 表示整行.。字段变量与其他变量相比没什么不同 — 它们也可以用在算术或字符串运算中, 也可以被赋值。
内建变量表
变量 | 含义 | 默认值 |
---|---|---|
ARGC | 命令行参数的个数 | - |
ARGV | 命令行参数数组 | - |
FILENAME | 当前输入文件名 | - |
FNR | 当前输入文件的记录个数 | - |
FS | 控制着输入行的字段分割符 | " " |
NF | 当前记录的字段个数 | - |
NR | 到目前为止读的记录数量 | - |
OFMT | 数值的输出格式 | “%.6g” |
OFS | 输出字段分割符 | " " |
ORS | 输出的记录的分割符 | “\n” |
RLENGTH | 被函数 match 匹配的字符串的长度 | - |
RS | 控制着输入行的记录分割符 | “\n” |
RSTART | 被函数 match 匹配的字符串的开始 | |
SUBSEP | 下标分割符 | “\034” |
算术运算符
Awk 提供了通常的 +, -, *, /, %, ^ 运算符. 运算符 %计算余数: x%y 的值是 x 被 y 除的余数; 当 x 或 y 是负数时, x%y 的结果依赖于机器。^ 是指数运算符: x^y 表示 xy。所有的算术运算都用的是浮点数。
比较运算符
比较表达式指的是那些含有关系运算符, 或含有正则表达式匹配运算符的表达式。 关系运算符包括 <, <=, == (相等), != (不相等), >= 与 >。
表达式
- 初等表达式包括:
数值与字符串常量, 变量, 字段, 函数调用, 数组元素. - 可以把表达式组合起来的运算符包括:
赋值运算符 = += -= *= /= %= ^=
条件表达式 ?:
逻辑运算符 || (OR), && (AND), ! (NOT)
匹配运算符 ~ 和 !~
关系运算符 < <= == != > >=
拼接运算符 (没有显式的拼接运算符)
算术运算符 + - * / % ^
单目运算符 + 和 -
自增与自减运算符 ++ 和 – (包括前缀与后缀)
括号 (用于分组)
自增与自减
赋值语句:
n = n + 1
通常写成 ++n 或 n++, 这里用到了一元自增运算符, 作用是给变量加 1. 前缀形式 ++n 在传递 n 的值之前为 n 加 1; 后缀形式 n++ 在传递 n 的值之后, 为 n 加 1. 当 ++ 应用到赋值表达式时, 这会造成一些不一样的地方. 如果 n 的初始值为 1, 赋值语句 i = ++n 为 n 加 1, 并将新值 2 赋给i, 而 i = n++ 同样为 n 加 1, 但将旧值 1 赋给 i. 如果仅仅是给 n 加 1, 那么 n++ 与 ++n 没什么区别. 前缀与后缀自减运算符 – 给变量减 1, 除此之外与 ++ 相同。
内建算术函数
内建函数同内建变量一样,可以直接使用,这些函数都可以作为初等表达式使用。
内建算术函数表
函数 | 返回值 |
---|---|
atan2(y,x) | y/x 的反正切值, 定义域在 −π 到 π 之间 |
cos(x) | x的余弦值, x 以弧度为单位 |
int(x) | x的整数部分; 当 x 大于 0 时, 向 0 取整 |
log(x) | x的自然对数 (以 e 为底) |
rand() | 返回一个随机数 r, 0 ≤ r < 1 |
sin(x) | x 的正弦值, x 以弧度为单位. |
sqrt(x) | x 的方根 |
srand(x) | x 是 rand() 的新的随机数种子 |
字符串运算符
Awk 中只有一种字符运算符 — 拼接. 拼接并没有显式的运算符, 通过陆续写出字符串常量, 变量, 数组元素, 函数返回值, 与其他表达式, 就可以创建一个字符串。
内建字符串函数
没见字符串函数用来操作字符串,对应函数表如下:
内建字符串函数表
函数 | 描述 |
---|---|
gsub(r,s) | 将 $0 中所有出现的 r 替换为 s, 返回替换发生的次数. |
gsub(r,s,t ) | 将字符串 t 中所有出现的 r 替换为 s, 返回替换发生的次数 |
index(s,t) | 返回字符串 t 在 s 中第一次出现的位置, 如果 t 没有出现的话, 返回 0. |
length(s) | 返回 s 包含的字符个数 |
match(s,r) | 测试 s 是否包含能被 r 匹配的子串, 返回子串的起始位置或 0; 设置 RSTART 与 RLENGTH |
split(s,a) | 用 FS 将 s 分割到数组 a 中, 返回字段的个数 |
split(s,a,fs) | 用 fs 分割 s 到数组 a 中, 返回字段的个数 |
sprintf(fmt,expr-list) | 根据格式字符串 fmt 返回格式化后的 expr-list |
sub(r,s) | 将 $0 的最左最长的, 能被 r 匹配的子字符串替换为 s, 返回替换发生的次数. |
sub(r,s,t) | 把 t 的最左最长的, 能被 r 匹配的子字符串替换为 s, 返回替换发生的次数. |
substr(s,p) | 返回 s 中从位置 p 开始的后缀. |
substr(s,p,n) | 返回 s 中从位置 p 开始的, 长度为 n 的子字符串 |
表达式运算符
将运算符应用到常量, 变量, 字段, 数组元素, 函数, 和其他表达式中, 就可以构造出一个表达式,运算符按照优先级的升序排列. 优先级高的运算符优先求值,常用的表达式运算符总结如下:
表达式运算符表
操作 | 运算符 | 示例 | 含义 |
---|---|---|---|
赋值 | = += -= *= /= %= ^= | x *= 2 | x = x * 2 |
条件表达式 | ?: | x ? y : z | 若 x 为真, 则 y, 否则 z |
逻辑或 | || | x || y | 若 x 或 y 为真, 则为 1, 否则为 0 |
逻辑与 | && | x && y | 若 x 与 y 都为真, 则为 1, 否则为 0 |
数组成员 | in | i in a | 如果 a[i] 存在, 则为 1, 否则为 0 |
匹配 | ~ !~ | $1 ~ /x/ | 如果第一个字段包含 x, 则为 1, 否则为 0 |
关系运算 | < <= == != >= > | x == y | 如果 x 等于 y, 则为 1, 否则为 0 |
拼接 | “a” “bc” | “abc”; 不存在显式的拼接运算符 | |
减法, 加法 | + - | x + y | x 与 y 的和 |
乘法, 除法, 取模 | * / % | x % y | x 除以 y 的余数 |
单目加, 单目减 | + - | -x | x 的相反数 |
逻辑非 | ! | !$1 | 若 $1 为空或为 0, 则为 1, 否则为 0 |
指数运算 | ^ | x ^ y | x的y次方的值 |
自增, 自减 | ++ – | ++x, x++ | 为 x 加 1 |
字段 | $ | $i+1 | 1 加上第 i 个字段的值 |
组合 | ( ) | ($i)++ | 给第 i 个字段的值加 1 |
流程控制语句
Awk 提供花括号用于语句组合, if-else 用于决策, while, for, do 语句用于循环。
流程控制语句
- {statements}
语句组- if (expression)statements
如果 expression 为真, 执行 statements- if (expression) statements1 else statements2
如果 expression 为真, 执行 statements1, 否则执行 statements2- while (expression) statements
如果 expression 为真, 执行 statements; 然后重复前面的过程- for (expression1;expression2;expression3 ) statements
等价于 expression1; while (expression2) { statements; expression3}- for (variable in array) statements
轮流地将 variable 设置为 array 的每一个下标, 并执行 statements- do statements while (expression)
执行 statements; 如果 expression 为真就重复- break
马上离开最内层的, 包围 break 的 while, for 或 do- continue 开始最内层的, 包围 continue 的 while, for, 或 do 的下一次循环
- next
开始输入主循环的下一次迭代- exit
- exit expression
马上执行 END 动作; 如果已经在 END 动作内, 那就退出程序. 将 expression 作为程序的
退出状态返回.
delete 语句
一个数组元素可以通过
delete array[subscript]
删除,例如, 下面这个循环删除数组 pop 中所有的元素
for (i in pop)
delete pop[i]
split 函数
函数 split(str,arr,fs) 将字符串 str 切割成一个个字段, 并存储到数组 arr 中. 字段的个数作为函数的返回值返回. 第 3 个参数 fs 的字符串值作为字段的分割符. 如果第 3 个参数被忽略, 就使用 FS。
函数
split(“7/4/76”, arr, “/”)
将字符串 7/4/76 分割成 3 个字段, 使用 / 作为分割符; 它将 7 存储在 arr[“1”], 将 4 存储在arr[“2”], 将 76 存储在 arr[“3”]。
多维数组
Awk 不直接支持多维数组, 但是它利用一维数组来
近似模拟多维数组. 虽然你可以写出像 i, j 或 s, p, q, r 这样的多维数组下标, 实际上 awk
会将它们都拼接起来 (下标之间用一个分隔符分开), 合成一个单独的下标. 举例来说,
awk '
for (i = 1; i <= 10; i++)
for (j = 1; j <= 10; j++)
arr[i, j] = 0
'
创建了一个具有 100 个元素的数组, 下标具有形式 1,1, 1,2 等等. 在 awk 内部, 下标其实是以字符串的形式存储的。
自定义函数
除了内建函数, awk 还可以包含用户定义的函数. 定义函数的语句具有形式:
function name(patameter-list) {
statements
}
一个函数定义可以出现在任何 模式–动作 语句可以出现的地方. 于是, awk 程序的通常形式就变成了一系列的, 由换行符或分号分开的 模式–动作 语句与函数定义。
在一个函数定义中, 函数体左花括号后的换行符, 与右花括号前的换行符都是可选的, 参数列表由一系列的由逗号分开的变量名构成; 在函数体内部, 这些变量引用的是函数被调用时传递给它的参数。函数体可能包含一个 return 语句, 用于将程序执行流返回至调用函数 (返回的时候可以带有一个值)。
举个栗子:
# 自定义函数
function max(m, n) {
return m > n ? m : n
}
用户定义的函数可以在任何 模式–动作 语句的任何表达式中使用, 也可以在出现在任何函数体内。每一个对函数的使用都叫做一个 调用 (call)。举例来说, max 函数可以像这样调用:
{ print max($1, max($2, $3)) } # print maxinum of $1, $2, $3
function max(m, n) {
return m > n ? m : n
}
本节内容我们学习了 awk 的动作中支持的各种运算,包括表达式、常量、变量等,变量中包含了awk 的内建函数以及自定义函数,下节我们来学习awk 的输出。