The awk programming language, Chapter1 Note
中文版参考 https://github.com/wuzhouhui/awk
目录
AWK程序的结构
emp.data 包含有名字、每小时工资(以美元为单位)、工作时长、每一行代表一个雇员的记录
Beth 4.00 0
Dan 3.75 0
Kathy 4.00 10
Mark 5.00 20
Mary 5.50 22
Susie 4.25 18
现在你想打印每位雇员的名字以及他们的报酬(每小时工资乘以工作时长), 而
(1)打印工作时长大于零的雇员及其报酬
awk '$3 > 0 { print $1, $2 * $3 }' emp.data
Output:
Kathy 40
Mark 100
Mary 121
Susie 76.5
说明:
运行的程序用单引号包围起来,从输入文件emp.data 获取数据
运行的程序由一个单独的模式-动作语句(pattern-action statement) 组成
模式 "$3 > 0" 扫描每一个输入行,如果该行的第三列大于零, 则动作
awk 的基本操作 陆续地扫描每一行, 搜索可以被模式匹配的行;每匹配一个模式 => 动作执行
awk 程序是由一个或多个模式-动作语句组成的序列;在一个模式–动作语句中, 模式或动作可以省略其一, 但不能两者同时被省略
运行AWK程序
运行一个awk 程序有多种方式
(1)awk 'program' <input files...>
(2)也可以在命令行上省略输入文件, 只要键入
awk 'program'
在这种情况下, awk 会将program 应用到你接下来在终端输入的内容上面, 直到键入一个文件结束标志(Unix系统是组合键Control-d)
# awk '$3 > 0 { print $1, $2 * $3 }'
Beth 4.00 0
Dan 3.75 0
Kathy 4.00 10
Kathy 40 --> 终端输出
Mark 5.00 20
Mark 100 --> 终端输出
...
(3)如果程序比较长, 可以将其放在一个文件中
programfile内容为
$3 > 0 { print $1, $2 * $3 }
执行
# awk -f programfile emp.data
输出
Kathy 40
Mark 100
Mary 121
Susie 76.5
说明:选项-f --> awk 从文件中提取程序
输出
简单的输出print
(1)打印每一行: { print } 或 { print $0 }
# awk '{ print }' emp.data 或者
# awk '{ print $0 }' emp.data
输出
Beth 4.00 0
Dan 3.75 0
Kathy 4.00 10
Mark 5.00 20
Mary 5.50 22
Susie 4.25 18
说明: $0 表示一整行
(2)打印某些字段
{ print $1, $3 }
==> 打印第一个和第三个字段
说明:在print 语句中由逗号分隔的表达式, 在输出时默认用一个空格符分隔; 由print 打印的每一行都由一个换行符终止
(3)NF, 字段的数量
(4)NR, 行号
(5)将文本放入输出中
# awk '{ print NR, "total pay for", $1, "is", $2 * $NF }' emp.data
输出
1 total pay for Beth is 0
2 total pay for Dan is 0
3 total pay for Kathy is 40
4 total pay for Mark is 100
5 total pay for Mary is 121
6 total pay for Susie is 76.5
格式化输出printf
# awk '{ printf("total pay for %s is $%.2f\n", $1, $2 * $3) }' emp.data
输出
total pay for Beth is $0.00
total pay for Dan is $0.00
total pay for Kathy is $40.00
total pay for Mark is $100.00
total pay for Mary is $121.00
total pay for Susie is $76.50
说明:使用printf 不会自动产生空格符或换行符
# awk '{ printf("%-8s $%6.2f\n", $1, $2 * $3) }' emp.data
输出
Beth $ 0.00
Dan $ 0.00
Kathy $ 40.00
Mark $100.00
Mary $121.00
Susie $ 76.50
说明:
%-8s, 将名字左对齐输出, 占用8个字符的宽度
%6.2f, 将报酬以带有两位小数的数值格式打印出来, 占用6个字符的宽度
排序输出
# awk '{ printf("%6.2f | %s\n", $2 * $3, $0) }' emp.data
输出
0.00 | Beth 4.00 0
0.00 | Dan 3.75 0
40.00 | Kathy 4.00 10
100.00 | Mark 5.00 20
121.00 | Mary 5.50 22
76.50 | Susie 4.25 18
选择
通过文本内容选择
# awk '$1 == "Susie"' emp.data 或者使用正则表达式
# awk '/Susie/' emp.data
输出
Susie 4.25 18
模式的组合
逻辑运算符包括 &&, ||, 和 !
# awk '$2 >= 4 && $2 * $3 >= 50' emp.data
Mark 5.00 20
Mary 5.50 22
Susie 4.25 18
# awk '$2 >= 4 || $2 * $3 >= 50' emp.data 说明:两个条件都满足的行只输出一次
Beth 4.00 0
Kathy 4.00 10
Mark 5.00 20
Mary 5.50 22
Susie 4.25 18
# awk '$2 >= 4 {print} $2 * $3 >= 50 {print} ' emp.data 说明:两个条件都满足输出两次
Beth 4.00 0
Kathy 4.00 10
Mark 5.00 20
Mark 5.00 20
Mary 5.50 22
Mary 5.50 22
Susie 4.25 18
Susie 4.25 18
数据验证
数据验证在本质上是否定: 打印可疑行
NF != 3 { print $0, "number of fields is not equal to 3" }
$2 < 3.35 { print $0, "rate is below minimum wage" }
$2 > 10 { print $0, "rate exceeds $10 per hour" }
$3 < 0 { print $0, "negative hours worked" }
$3 > 60 { print $0, "too many hours worked" }
BEGIN、END
BEGIN 在第一个输入文件的第一行之前匹配
END 在最后一个输入文件的最后一行被处理之后匹配
例如,输出数据前打印表头
# awk 'BEGIN { print "NAME RATE HOURS"; print "" } {print}' emp.data
NAME RATE HOURS
Beth 4.00 0
Dan 3.75 0
Kathy 4.00 10
Mark 5.00 20
Mary 5.50 22
Susie 4.25 18
说明:可以在同一行放置多个语句, 语句之间用分号分开
用AWK计算
计数
# cat countWorkUp15
$3 > 15 { emp = emp + 1 }
END { print emp, "employees worked more than 15 hours" }
# awk -f countWorkUp15 emp.data
3 employees worked more than 15 hours
说明:当awk 的变量作为数值使用时, 默认初始值为0, 所以无需初始化emp
计算总和与平均数
# cat averPay
{ pay = pay + $2 * $3 }
END { print NR, "employees"
print "total pay is", pay
print "average pay is", pay / NR
}
# awk -f averPay emp.data
6 employees
total pay is 337.5
average pay is 56.25
取最大值
# cat maxHourRate
$2 > maxrate { maxrate = $2; maxemp = $1 }
END { print "highest hourly rate:", maxrate, "for", maxemp }
# awk -f maxHourRate emp.data
highest hourly rate: 5.50 for Mary
说明:如果有多个雇员都拥有相同的最高每小时工资, 这个程序只会打印第一个人的名字
字符串拼接
# awk '{ names = names $1 " " } END { print names }' emp.data
输出
Beth Dan Kathy Mark Mary Susie
打印最后一行
# awk 'END { print last }' emp.data 说明: 输出是空行
# awk '{ last = $0 } END { print last }' emp.data
Susie 4.25 18
说明:在END 动作里, NR的值保留了下来, $0的值没有保留
内建函数length
# cat -A emp.data
Beth^I4.00^I0$
Dan^I3.75^I0$
Kathy^I4.00^I10$
Mark^I5.00^I20$
Mary^I5.50^I22$
Susie^I4.25^I18$
# awk '{ print $0,"--", length($0) }' emp.data
Beth 4.00 0 -- 11
Dan 3.75 0 -- 10
Kathy 4.00 10 -- 13
Mark 5.00 20 -- 12
Mary 5.50 22 -- 12
Susie 4.25 18 -- 13
说明:$0 不包含换行符
流程控制语句
If-Else 语句
# cat test1
$2 > 6 { n = n + 1; pay = pay + $2 * $3 }
END { if (n > 0)
print n, "employees, total pay is", pay,
"average pay is", pay/n
else
print "no employees are paid more than $6/hour"
}
# awk -f test1 emp.data
输出
no employees are paid more than $6/hour
While 语句
价值计算的公式
value.txt内容如下
# interest1 - compute compound interest
# input: amount rate years
# output: compounded value at the end of each year
{ i = 1
while (i <= $3) {
printf("\t%.2f\n", $1 * (1 + $2) ^ i)
i = i + 1
}
}
键入三个数, 看一下不同的本金, 利率和时间会产生怎样的结果
例如, $1000 在6% 与12% 的利率下, 在5 年的时间的升值如下
# awk -f value.txt
1000 0.06 5
1060.00
1123.60
1191.02
1262.48
1338.23
1000 0.12 5
1120.00
1254.40
1404.93
1573.52
1762.34
For 语句
可以实现与上例相同的功能
value2.txt内容如下
# interest2 - compute compound interest
# input: amount rate years
# output: compounded value at the end of each year
{ for (i = 1; i <= $3; i = i + 1)
printf("\t%.2f\n", $1 * (1 + $2) ^ i)
}
# echo "1000 0.06 5" > test
# awk -f value2.txt test
1060.00
1123.60
1191.02
1262.48
1338.23
数组
如下实现了从最后一行倒着打印输入文件
reversePrint内容如下
# reverse - print input in reverse order by line
{ line[NR] = $0 } # remember each input line
END { i = NR # print lines in reverse order
while (i > 0) {
print line[i]
i = i - 1
}
}
# awk -f reversePrint emp.data
Susie 4.25 18
Mary 5.50 22
Mark 5.00 20
Kathy 4.00 10
Dan 3.75 0
Beth 4.00 0