1 说明
man awk
:
awk - pattern scanning and processing language
awk的名字来源于三位创始人的姓氏:Alfred Aho,Peter Weinberger, 和 Brian Kernighan。类似sed,awk每次读取一行为进行处理,但处理的最小单位是字段(也就是列,通常使用空格或制表符分隔),因此适合处理表格一类的数据。但同时awk不止是一个程序/命令,还是一门程序设计(脚本式的)语言,常应用于各种计算和数据处理任务。
参考资料
- mylxsw, 三十分钟学会AWK, 2016-10-31
- AWK入门指南: 中文翻译,不完整, 2017-10-26
- Jiavan, awk 入坑指北, 2018-01-10
- vvpale, linux基础命令介绍八:文本分析 awk, 2016-11-23
- wuzhouhui的github仓库, The AWK Programming Language
- GNU awk用户手册
2 基本
awk命令的基本形式:
awk [options] SCRIPT [file]
SCRIPT
结构:
脚本通常分为三部分,其中BEGIN,END部分是可选部分;当只有BEGIN部分是可以没有输入文件。其中:
-
BEGIN
的action会在输入开始之前执行; -
ENG
的action会在所有行处理之后执行。
另外,awk作为编程语言,可以定义函数,可以包含注释(通常在文件中,以保持可读性和可重用性),使用#
注释该字符所在的行(不包括之前的字符)。
BEGIN {action}
pattern {action}
END {action}
awk处理的基本流程:
- 按行读取(从文件或标准输入等),每一行称为一条记录record,使用
$0
表示;
1.1 当文件缺省时,从标准输入中读取;使用Ctrl+D终止程序 - 对每一行执行SCRIPT,包括检查是否满足pattern,满足则执行action部分;其中:
2.1 pattern省略是表示没有条件限制;
2.2 action省略是表示执行{print}
即打印当前记录,两者不可同时省略。
选项
常用的选项包括:
-
-f progfile; --file=progfile
从file中读取awk指令。 -
-F fs; --field-separator=fs
指定分隔符:
awk -F: '{print $1, $NF}' file.txt
# -F后的内容是正则表达式形式
awk -F '[.,:]' '{print $1, $NF}' file.txt
-
-v var=val; --assign=var=val
设定变量
awk -v n=5 'BEGIN{for(i=0;i
-
-d[=file]; --dump-variables[=file]
输出排序后的全局变量和值;默认文件输出文件是awkvars.out
-
-p[file]; --profile[=file]
格式化awk程序,默认输出的文件是awkprof.out
awk --profile 'BEGIN{printf"---|Header|--\n"} {print}
END{printf"---|Footer|---\n"}' marks.txt > /dev/null
其他选项
-
-V, --version
版本信息 -
--help
帮助信息
SCRIPT之使用pattern
过滤
- pattern部分可以由多个正则表达式或算式由逻辑运算符连接而成
- pattern之间可以使用,分隔,表示两个匹配模式之间的记录
例子:
awk 'BEGIN{print "----"} {print $1} END{print "-----"}'
awk '/^root/,/^adm/' /etc/passwd
SCRIPT之使用action
执行
pattern
之后的{}
的内容表示 action
;当actiono多个时可以使用;
或换行进行分隔。
这里先简单介绍print:
print
表示输出,期间可以使用$数组, 字符串需要使用引号包围。
- 字段之间使用空格或,进行分隔
- 可以重定向输出
>
和>>
保存输出
此外还有格式化输出的printf
。
另外awk中自定义变量,使用=进行赋值(直接使用,不需要向shell一样使用$
标志),数组,条件语句等。详细的见【程序设计部分】。
例子:
# $5即文件大小字段,$NF即文件名字段
# && 逻辑与;`/../`表示正则表达式; ~表示匹配
ls -l *|awk '$5>20 && $NF ~ /txt$/'
# 列出第1,3列
last -n 5 | awk '{print $1 "\t" $3}'
# 设置分隔符是:, 第3列值小于10是打印第1列(账号)和第3列(UID)
cat /etc/passwd | awk '{FS=":"} $3 < 10 {print $1 "\t " $3}'
# 增加显示第一行(root);默认第一行是字段说明所以不直接处理
cat /etc/passwd | awk 'BEGIN{FS=":"} $3 < 10 {print $1 "\t " $3}'
# 复杂例子;从文件中读取awk
# test.awk中的内容
BEGIN{
$1=1
$2=1
OFS=","
for(i=3;i<=10;i++)
{
$i=$(i-2)+$(i-1)
}
print
}
# 打印斐波那契数列前10项
awk -f test.awk
# 定义变量
awk '{a=$1} {print a,$5}' file.txt
awk -v a="name" '{print a}'
# 使用数组
$ awk -F ':' '{a[$NF]++}END{for(i in a) print i,a[i]}' /etc/passwd
# 使用条件语句
netstat -antp | awk '{if($6=="LISTEN"){x++}else{y++}}END{print x,y}'
3 程序设计
内建变量
内建变量 | 说明 |
---|---|
$0 | 当前处理记录(即当前读取的一行内容) |
$1-$n | 当前记录的第n个字段(列) |
FS | 字段/列分隔符,默认是空格或制表符 |
NF | 当前记录的字段数目 |
NR | 已经处理的记录条数,即当前行号 |
FNR | 当前记录数,是每个文件的行号,NR是总数 |
RS | 记录/行之间分隔符,默认是换行符\n |
CONVFMT | 数字转换格式 %.6g |
OFS | 输出字段分隔符,默认是空格 |
ORS | 输出记录分隔符,默认是换行符\n |
FIELDWIDTHS | 输入字段宽度的空白分隔字符串 |
OFMT | 数字的输出格式,%.6g |
ARGC | 命令行参数个数 |
ARGV | 命令行参数数组 |
FILENAME | 当前输入文件名字 |
ARGIND | 当前被处理文件的ARGV标志符 |
ENVIRON | UNIX/SHELL环境变量,数组形式 |
ERRNO | UNIX系统错误消息 |
RSTART | 被匹配函数匹配的字符串首 |
RLENGTH | 匹配函数匹配的字符串长度 |
IGNRECASE | 记录为真代表忽略大小写匹配 |
SUBSEP | \034 (0x1C ;文件分割符) |
PROCINFO | 当前运行程序进程信息数组 |
此外还有一些GNU awk扩展: 略
运算符
基本和C语言一致,基本的有:
- 赋值:=
- 算术运算符:+-*/%;单目 ++, --, -(相反数), ^(指数)
- 关系运算符:>, >=, <, <=, ==, !=
- 逻辑运算符:&&, ||, !; 常在条件、循环等语句中使用
- 三目:
(a>b)? max=a:max=b;
- 字符串连接:
str3 =str1 str2
- 数组:[]成员操作符
- 数字索引的数组(可以不连续),字符串索引的数组(类似字典);
不需要预先声明
- 数字索引的数组(可以不连续),字符串索引的数组(类似字典);
- 正则:
~
和!~
分别表示匹配和不匹配:$0 ~ /[a-z]/
- 正则表达式:需要使用斜杠
/
对表达式进行包围
- 正则表达式:需要使用斜杠
例子:
awk 'BEGIN { a = 50; b = 20; print "(a + b) = ", (a + b) }'
控制语句
- 条件语句: if-else
- 循环语句:for, while
-
{}
表示语句块,可以进一步嵌套
例子:来自
# 计算复利
# 输入: 钱数 利率 年数
# 输出: 复利值
{ i = 1
while (i <= $3) {
printf("\t%.2f\n", $1 * (1 + $2) ^ i)
i = i + 1
}
}
用户自定义函数
function function_name(argument1, argument2, ...) {
function body
}
例子:来自
# 脚本文件
# Returns minimum number
function find_min(num1, num2){
if (num1 < num2)
return num1
return num2
}
# Returns maximum number
function find_max(num1, num2){
if (num1 > num2)
return num1
return num2
}
# Main function
function main(num1, num2){
# Find minimum number
result = find_min(10, 20)
print "Minimum =", result
# Find maximum number
result = find_max(10, 20)
print "Maximum =", result
}
# Script execution starts here
BEGIN {
main(10, 20)
}
调用外部shell程序/命令
- 管道
- system函数
例子:
# 管道方式
while (more stuff to do)
print command | "/bin/sh"
close("/bin/sh")
# system函数
END { system("date | mail -s 'awk run done' root")}
4 内建函数
数学相关函数
- exp, log
- int, sqrt,
- rand, srand
- sin, cos, atan2
字符串相关
- length() 获得字符串长度
awk -F: '{if(length($1)>=16) print}' /etc/passwd
- split()将字符串按分隔符分隔,并保存至数组
head -1 /etc/passwd|awk '{split($0,arr,/:/);for(i=1;i<=length(arr);i++) print arr[i]}'
- sub(regex,substr,string) 替换string中匹配regex的第一项为substr;string为空则默认是$0;
echo 178278 world | awk 'sub(/[0-9]+/,"hello")'
- gsub: 类似sub,全部替换
- substr
- tolower
- match
时间
- systime
- mktime(datespec)
- strftime([format [, timestamp[, utc-flag]]])
bit操作
- and, or, xor
- compl(val): Return the bitwise complement of val.
- 移位 lshift, rshift
其他(IO等)
- close(expr) 关闭管道文件
- delete 删除数组元素
- exit 退出脚本执行并返回状态码
- getline 读取下一行内容
- next
- return
- system(cmd) 执行外部shell的cmd命令,返回的结果0表示成功