sed与awk-第七章-编写awk脚本
第七章 编写awk脚本
本章讲 POSIX‑awk(标准awk,不是gawk特有扩展)。
遵守规则
- awk有完整语法规则,参考附录二快速参考。
- 学习方式:先写脚本实践,遇到问题再回头看语法手册。
- 常见坑:漏掉空格、括号、引号,语法报错。
- 注释:
#开头,到行尾结束;awk脚本任意位置都可以写注释,这点和sed不一样。
Hello,World
awk是输入驱动型语言:没有输入行,主循环代码不会执行。
- 每一行输入都执行print输出字符串
|
|
输入行会被读取,但不会自动打印;只打印写死的字符串。
- 无参数print,原样输出输入行
|
|
- BEGIN块:在读入任何输入行之前执行,但仅执行一次,不需要输入文件也能运行
|
|
如果整个脚本只有BEGIN,awk不会去读取任何输入文件。就算后边跟着test文件,也不会去读test文件。
awk程序设计模型
awk内置主输入循环,不需要你手写读文件、读行代码。 整个awk脚本分为3部分:
| 模块 | 执行时机 |
|---|---|
BEGIN {…} |
处理输入之前,读取任何输入行之前执行一次 |
模式/动作(pattern {action}) |
主输入循环:对每一行输入循环执行 |
END {…} |
全部输入读完之后,退出前执行一次 |
流程:BEGIN → 循环读每一行,执行模式动作 → END。 模式:条件判断;动作:大括号
{}里面的代码; 如果只写模式不写动作,默认动作是打印匹配行。
示例:匹配空行就打印提示
|
|
一行输入可以匹配多条模式规则,多条动作都会执行。
程序脚本的注释
#行注释,从#到行末尾。
脚本文件里可以随意加注释;命令行直接写awk单行脚本时,注意shell引号干扰,慎用#注释。
记录和字段
awk默认:
- 记录(record):一行输入,
$0代表整条记录 - 字段(field):被分隔符切分出来的单词;默认分隔符:连续空格/制表符
$1第1字段,$2第2字段……$NF最后一个字段。
字段引用
示例names文件内容
|
|
print里面的逗号,输出时自动用 输出字段分隔符OFS(默认空格) 隔开。
字段编号可以是表达式计算结果:
修改输入字段分隔符FS
- 命令行参数
-F
- 在
BEGIN块设置内置变量FS(推荐写在脚本内部)
FS的三种工作模式(重点)
- FS = " "(默认,一个空格)
- 前导、末尾空格制表全部忽略;
- 任意多个连续空格、tab都当做一个分隔符。
- FS设置单个字符,例如 FS=":"、FS="\t"
- 每一个该字符都是分隔符;两个连续分隔符之间产生空字段。
- 例:
abc\t\tdef,FS="\t" → 切分成3段:abc、空字符串、def
- FS设置为正则表达式(长度>1)
|
|
- 把匹配正则的子串作为分隔符。
字段匹配操作符
~:字段匹配正则;!~:字段不匹配正则
表达式
表达式由:常量、变量、运算符、函数、正则组成。
常量
- 字符串常量:必须双引号
"hello" - 数字常量:
100、3.14 - 字符串拼接:直接放一起,不需要运算符,空格代表拼接
|
|
变量
- 变量命名:字母数字下划线,不能数字开头,区分大小写。
- 不需要声明定义,不用指定类型。变量同时保存字符串值和数字值,awk自动根据上下文选用。
- 未初始化的变量:字符串是空;当做数字时值等于0。
表7‑1 转义序列
| 序列 | 描述 | 示例 |
|---|---|---|
\a |
报警BEL字符 | 蜂鸣 |
\b |
退格 | |
\f |
换页 | |
\n |
换行 | |
\r |
回车 | |
\t |
水平制表 | |
\v |
垂直制表 | |
\ddd |
1‑3位八进制字符 | \141 代表a |
\xhex |
十六进制字符(非POSIX扩展) | \x61代表a |
\c |
字面输出c |
\xhex不属于POSIX标准,部分awk版本不支持。
表7‑2 算术运算符
| 操作符 | 描述 | 备注 |
|---|---|---|
+ |
加 | |
- |
减 | |
* |
乘 | |
/ |
除 | |
% |
取模(余数) | |
^ |
取幂 | POSIX标准 |
** |
取幂 | 扩展,不可移植,不推荐 |
表7‑3 赋值运算符
| 操作符 | 定义 |
|---|---|
++ |
变量自增1 |
-- |
变量自减1 |
+= |
x += n 等价 x = x + n |
-= |
x -= n 等价 x = x - n |
*= |
x *= n 等价 x = x * n |
/= |
x /= n 等价 x = x / n |
%= |
x %= n 等价 x = x % n |
^= |
x ^= n 等价 x = x ^ n |
**= |
取幂赋值(扩展,不可移植) |
++--区分前缀、后缀:
++x:先自增,再返回值(前缀)x++:先返回原值,再自增(后缀)
示例:统计空行总数
示例:学生成绩求平均值 输入grades文件
脚本grades.awk
awk内置系统变量
| 变量 | 含义 |
|---|---|
FS |
输入字段分隔符,默认空格;BEGIN中修改生效,只影响下一行 |
OFS |
输出字段分隔符,默认空格;print逗号输出用这个隔开 |
RS |
输入记录分隔符,默认换行\n;RS=""代表按空行分割多条记录 |
ORS |
输出记录分隔符,默认换行\n |
NF |
当前这一行切割出来的字段的数量;$NF取最后一个字段 |
NR |
行号计数器。读到第 1 行 NR=1;读到第 2 行 NR=2;跨多个文件会持续累加。 |
FNR |
当前文件内的记录行号;每个新文件从1重新计数 |
FILENAME |
当前正在读取的输入文件名 |
CONVFMT |
POSIX,数字转字符串格式,默认%.6g |
OFMT |
print输出数字格式(老版本awk) |
重要:POSIX awk中修改FS,不会改变当前已经读入的行,只会对下一行生效。
示例,打印总行数
多行记录(记录之间空行分隔)
场景:一条记录跨多行,记录与记录之间用空行隔开。 设置:
还可以修改OFS、ORS改变多行记录输出格式。
支票簿结算示例
输入:第一行为初始余额,后续每行:支票号、商户、金额(负数代表支出)
脚本 checkbook.awk
next:立刻结束当前行处理,直接跳到文本读取第 2 行,下面大括号的代码完全跳过,不执行。
关系操作符与布尔操作符
表7‑4 关系操作符
| 操作符 | 描述 |
|---|---|
< |
小于 |
> |
大于 |
<= |
小于等于 |
>= |
大于等于 |
== |
等于判断;不要和赋值=搞混 |
!= |
不等于 |
~ |
匹配正则 |
!~ |
不匹配正则 |
示例:只打印恰好6个字段的记录
|
|
注意:
=是赋值;==才是相等判断,新手高频错误。
表7‑5 布尔操作符
| 操作符 | 定义 |
|---|---|
&& |
逻辑与(两边条件全部为真才成立) |
| |
逻辑或(任意一个条件为真就成立) |
! |
逻辑非,取反 |
示例复合条件
&&优先级高于||,复杂条件建议写括号明确顺序。
ls‑l输出处理示例 filesum脚本
需求:读取ls -l输出,统计普通文件大小总和,区分普通文件、目录,忽略total汇总行。
|
|
格式化打印 printf
print:自动追加ORS换行;printf:不会自动输出换行,必须手动写\n。
语法:
表7‑6 printf格式格式符
| 字符 | 定义 |
|---|---|
c |
ASCII字符 |
d |
十进制整数 |
i |
十进制整数(POSIX新增) |
e |
科学计数法e形式 |
E |
科学计数法E大写形式 |
f |
浮点数小数形式 |
g |
自动选用e/f,去除多余末尾0 |
G |
自动选用E/f,去除多余末尾0 |
o |
无符号八进制 |
s |
字符串 |
u |
无符号十进制 |
x |
小写十六进制 a‑f |
X |
大写十六进制 A‑F |
%% |
输出字面%符号 |
修饰符完整格式:
%[-][宽度][.精度]格式符
-:不加-默认右对齐;写-代表左对齐- 宽度:输出占多少字符位,不足补空格
.精度:对浮点数是小数点位数;字符串代表最多输出多少字符
示例
%*.*f中*的含义,*代表:宽度 / 精度不从格式字符串写死,从后面的参数取数值。
filesum使用printf对齐输出示例
向脚本传递参数
1. 命令行直接赋值变量(写在脚本后,文件名之前)
|
|
限制:这种方式的变量,BEGIN块里面拿不到值!
原理:awk把high=10当成伪文件名;处理到这条赋值的时候才生效,此时已经开始处理输入。
awk 做了一条特殊判断:如果某个参数格式是变量名=值,里面包含=,不把它当做真正要打开的磁盘文件,当做「伪文件 / 赋值参数」。不会去磁盘找名叫high=10的文件,所以不会报 “文件不存在” 错误。
2. -v参数(解决BEGIN拿不到变量的问题,推荐)
-v 变量=值,写在脚本/程序代码前面;在BEGIN执行前完成赋值。
|
|
多个变量就写多个
‑v。
shell脚本把shell参数传给awk
shell脚本acro示例(acro缩写查询工具)
调用:acro CICS
检索多行文本块实战
原始问题:输入文件多个文本块,块之间空行分隔;需要搜索关键词,打印整个匹配的块。
利用 RS=""(空行做记录分隔符)实现:
|
|
第七章 核心速记清单
- awk三大块:
BEGIN {}(处理输入前)、主循环模式/动作、END{}(全部读完后);awk自带读行主循环,不用手写读文件。 $0完整记录;$1/$2…字段;$NF最后字段;NF字段总数;NR全局行号;FNR单文件行号。- FS输入分隔符、OFS输出分隔符;RS输入记录分隔符,
RS=""空行分割多条记录。 - 变量不用声明,未初始化数字为0,字符串为空;
++区分前后缀。 - 模式:
~ /正则/匹配;!~不匹配;==判断相等,不要写成=赋值。 - 布尔:
&&与,||或,!非;&&优先级高于||。 next:立刻结束当前行,直接读下一行。printf不会自动换行;格式:%[-]宽度.精度类型。- 传参:普通变量赋值BEGIN不可见;必须用
‑v才能在BEGIN拿到外部变量。 - 只写模式不写动作:默认动作是打印整行。
文章作者 会写代码的小郎中
上次更新 2011-08-06
许可协议 CC BY-NC-ND 4.0