AWK是一门解释型的编程语言。在文本处理领域它是非常强大的,它的名字来源于它的三位作者的姓氏:Alfred Aho, Peter Weinberger 和 Brian Kernighan。AWK遵循了非常简单的工作流 - 读取,执行和重复
1. awk命令格式和选项
awk [options] 'script' var=value file(s)
awk [options] -f scriptfile var=value file(s)
1.1 常用命令选项
选项 | 描述 |
---|---|
-F or --field-separator fs | 指定输入文件折分隔符,fs是一个字符串或者是一个正则表达式,如-F: |
-v var=value or --asign var=value | 赋值一个用户定义变量 |
-f scripfile or --file scriptfile | 从脚本文件中读取awk命令 |
1.2 awk的环境变量
变量 | 描述 |
---|---|
$n | 当前记录的第n个字段,字段间由FS分隔 |
$0 | 完整的输入记录 |
ARGC | 命令行参数的数目 |
ARGIND | 命令行中当前文件的位置(从0开始算) |
ARGV | 包含命令行参数的数组 |
BINMODE | 在非POSIX系统上指定对所有的文件I/O采用二进制模式。 |
CONVFMT | 数字转换格式(默认值为%.6g) |
ENVIRON | 环境变量关联数组 |
ERRNO | 最后一个系统错误的描述 |
ERRORNO | 一个代表了getline跳转失败或者是close调用失败的错误的字符串 |
FIELDWIDTHS | 字段宽度列表(用空格键分隔),设置了空格分隔的字段宽度变量列表的话,GAWK会将输入解析为固定宽度的字段,而不是使用FS进行分隔 |
FILENAME | 当前文件名 |
FNR | 同NR,但相对于当前文件 |
FS | 字段分隔符(默认是任何空格) |
IGNORECASE | 如果为真,则进行忽略大小写的匹配 |
NF | 当前记录中的字段数 |
NR | 当前记录数 |
OFMT | 数字的输出格式(默认值是%.6g) |
OFS | 输出字段分隔符(默认值是一个空格) |
ORS | 输出记录分隔符(默认值是一个换行符) |
RLENGTH | 由match函数所匹配的字符串的长度 |
RS | 记录分隔符(默认是一个换行符) |
RSTART | 由match函数所匹配的字符串的第一个位置。 |
SUBSEP | 数组下标分隔符(默认值是\034) |
2. awk 命令结构 - 模式和操作
2.1. 模式
模式和操作都是可选的,如果没有模式,则action应用到全部记录;如果没有action,则输出匹配全部记录。默认情况下,每一个输入行都是一条记录,但用户可通过RS变量指定不同的分隔符进行分隔。
模式可以是以下任意一个,以test.txt为例:
zemi boy 30 4
sjq boy 30 5
cg girl 28 10
xl boy 34 10
- /正则表达式/:使用通配符的扩展集。关系表达式:可以用下面运算符表中的关系运算符进行操作,可以是字符串或数字的比较,如$2>$1选择第二个字段比第一个字段大。
awk '$2 > $1 {print }' test.txt
awk '{print ($2 > 29 ? "ok "$1: "error"$1)}' test.txt
awk '/a/{++cnt} END {print
"Count = ", cnt}' test.txt //统计匹配的行
- 模式匹配表达式:用运算符~(匹配)和~!(不匹配)。
awk '$2 ~/[boy]/{print}' test.txt
- 模式:指定一个行的范围。该语法不能包括BEGIN和END模式。
awk 'NR%2 == 0{print}' test.txt
- BEGIN:让用户指定在第一条输入记录被处理之前所发生的动作,通常可在这里设置全局变量。BEGIN语句块在程序开始的使用执行,它只执行一次,在这里可以初始化变量。BEGIN是AWK的关键字,因此它必须为大写,注意,这个语句块是可选的。
awk 'BEGIN {printf "name sex age work\n"} {print }' test.txt
- BODY:语句块中的命令会对输入的每一行执行,我们也可以通过提供模式来控制这种行为。注意,BODY语句块没有关键字。
- END:让用户在最后一条输入记录被读取之后发生的动作。END语句块在程序的最后执行,END是AWK的关键字,因此必须为大写,它也是可选的。
awk '/boy/{++cnt} END {print "Count = ", cnt}' test.txt
2.2 操作
操作由一人或多个命令、函数、表达式组成,之间由换行符或分号隔开,并位于大括号内。主要有四部份:
- 变量或数组赋值
- 输出命令
- 内置函数
- 控制流命令
3. 记录和域
3.1 记录
awk把每一个以换行符结束的行称为一个记录。默认的输入和输出的分隔符都是回车,保存在内建变量ORS和RS中。
$0变量:它指的是整条记录,将输出test文件中的所有记录,如$ awk '{print $0}' test.txt
变量NR:一个计数器,每处理完一条记录,NR的值就增加1。将输出test文件中所有记录,并在记录前显示记录号$ awk '{print NR,$0}' test.txt
3.2 域
记录中每个单词称做“域”,默认情况下以空格或tab分隔。awk可跟踪域的个数,并在内建变量NF中保存该值。将打印test文件中第一和第三个以空格分开的列(域)$ awk '{print $1,$3}' test.txt
3.3 范围模板
范围模板匹配从第一个模板的第一次出现到第二个模板的第一次出现之间所有行。如果有一个模板没出现,则匹配到开头或末尾。如:$ awk '/30/,/28/' test.txt
显示30第一次出现到28第一次出现之间的所有行。
4. awk编程
4.1 变量
变量的命名可以通过 -v var = value,也可以在操作中直接用
$ awk -v count=0 '/boy/ {count++} END { print count}' test.txt
$
awk '$1 ~/boy/{count = $2 + $3;print count}' test.txt
4.2 关联数组
$ awk '{p[NR]=$1} END {for(i=0;i<NR;i++) print i,p[i]}' test.txt
4.3 重定向和管道
使用shell的重定向符进行重定向输出
$ awk '$1=100 {print $1>"output_file"}' test.txt
上式表示如果第一个域的值等于100,则把它输出到output_file中。也可以用>>来重定向输出,但不清空文件,只做追加操作。
system函数可以在awk中执行linux的命令。如:$ awk 'BEGIN{system("clear")'
fflush函数用以刷新输出缓冲区,如果没有参数,就刷新标准输出的缓冲区,如果以空字符串为参数,如fflush(""),则刷新所有文件和管道的输出缓冲区。
5 条件语句
awk中的条件语句是从C语言中借鉴过来的,可控制程序的流程。
if语句
{if (expression) {
statement; statement; ...
}
}
$ awk '{if ($1 <$2) print $2 "too high"}' test
$ awk '{if ($1 < $2) {count++; print "ok"}}' test
if/else语句,用于双重判断
{if (expression){
statement; statement; ...
}
else{
statement; statement; ...
}
}
$ awk '{if ($1 > 100) print $1 "bad" ; else print "ok"}' test
$ awk '{if ($1 > 100){ count++; print $1} else {count--; print $2}' test
if/else else if语句,用于多重判断`
{if (expression){
statement; statement; ...
}
else if (expression){
statement; statement; ...
}
else if (expression){
statement; statement; ...
}
else {
statement; statement; ...
}
}
6 循环
awk有三种循环:while循环;for循环;special for循环
$ awk '{ i = 1; while ( i <= NF ) { print NF,$i; i++}}' test
$ awk '{for (i = 1; i<NF; i++) print NF,$i}' test
$ awk '/^tom/{name[NR]=$1}; END{for(i in name){print name[i]}}' test
说明:打印有值的数组元素。打印的顺序是随机的。
breadk continue语句
- break用于在满足条件的情况下跳出循环
- continue用于在满足条件的情况下忽略后面的语句,直接返回循环的顶端
如:
{for ( x=3; x<=NF; x++) if ($x<0){print "Bottomed out!"; break}}
{for ( x=3; x<=NF; x++) if ($x==0){print "Get next item"; continue}}
next语句从输入文件中读取一行,然后从头开始执行awk脚本。如:
{ if ($1 ~/test/){next} else {print}}
exit语句用于结束awk程序,但不会略过END块。退出状态为0代表成功,非零值表示出错。
7 数组
7.1 算术操作
基本操作算数操作符不多说,直接看例子,无非就是+-*/%
$ awk 'BEGIN { a = 50; b = 20; print "(a + b) = ", (a + b) }'
awk中的数组的下标可以是数字和字母
7.2 增减运算符:自增自减与C语言一致
awk 'BEGIN { a = 10; b = ++a; printf "a = %d, b = %d\n", a, b }'
awk 'BEGIN { a = 10; b = --a; printf "a = %d, b = %d\n", a, b }'
awk 'BEGIN { a = 10; b = a--; printf "a = %d, b = %d\n", a, b }'
awk 'BEGIN { a = 10; b = a++; printf "a = %d, b = %d\n", a, b }'
7.3 赋值操作符
awk 'BEGIN { age = "20"; print "My age is", age }'
awk 'BEGIN { cnt = 100; cnt -= 10; print "Counter =", cnt }'
7.4 关系操作符
$ awk 'BEGIN { a = 10; b = 10; if (a == b) print "a == b" }'
a == b
$ awk 'BEGIN { a = 10; b = 20; if (a != b) print "a != b" }'
a != b
$ awk 'BEGIN { a = 10; b = 20; if (a < b) print "a < b" }'
a < b
$ awk 'BEGIN { a = 10; b = 10; if (a <= b) print "a <= b" }'
a <= b
$ awk 'BEGIN { a = 10; b = 20; if (b > a ) print "b > a" }'
b > a
7.5 逻辑操作符
$ awk 'BEGIN {
num = 5; if (num >= 0 && num <= 7) printf "%d is in octal format\n", num
}'
5 is in octal format
$ awk 'BEGIN {
ch = "\n"; if (ch == " " || ch == "\t" || ch == "\n")
print "Current character is whitespace."
}'
Current character is whitespace.
$ awk 'BEGIN { name = ""; if (! length(name)) print "name is empty string." }'
name is empty string.
7.6 下标与关联数组
用变量作为数组下标。如:
$ awk {name[x++]=$2};END{for(i=0;i<NR;i++) print i,name[i]}' test.txt
数组name中的下标是一个自定义变量x,awk初始化x的值为0,在每次使用后增加1。第二个域的值被赋给name数组的各个元素。在END模块中,for循环被用于循环整个数组,从下标为0的元素开始,打印那些存储在数组中的值。因为下标是关健字,所以它不一定从0开始,可以从任何值开始。
special for循环用于读取关联数组中的元素。格式如下:
{for (item in arrayname){
print arrayname[item]
}
}
for循环方式,for (index_value in array) statement。如:
$ awk '{count[$1]++} END{for(name in count) print name,count[name]}' test
该语句将打印$1中字符串出现的次数。它首先以第一个域作数组count的下标,第一个域变化,索引就变化。
delete函数用于删除数组元素。如:
$ awk '{line[x++]=$1} END{for(x in line) delete(line[x])}' test
分配给数组line的是第一个域的值,所有记录处理完成后,special for循环将删除每一个元素。
8 awk的内建函数
8.1 字符串函数
- asort(arr [, d [, how] ])
- asorti(arr [, d [, how] ])
- gsub(regex, sub, string)
- index(str, sub)
- length(str)
- match(str, regex)
- split(str, arr, regex)
- sprintf(format, expr-list)
- strtonum(str)
- sub(regex, sub, string)
- substr(str, start, l)
- tolower(str)
- toupper(str)
- sub函数:匹配记录中最大、最靠左边的子字符串的正则表达式,并用替换字符串替换这些字符串。如果没有指定目标字符串就默认使用整个记录。替换只发生在第一次匹配的时候。格式如下:
sub (regular expression, substitution string):
sub (regular expression, substitution string, target string)
实例:
$ awk '{ sub(/test/, "mytest"); print }' testfile
$ awk '{ sub(/test/, "mytest"); $1}; print }' testfile
第一个例子在整个记录中匹配,替换只发生在第一次匹配发生的时候。如要在整个文件中进行匹配需要用到gsub
第二个例子在整个记录的第一个域中进行匹配,替换只发生在第一次匹配发生的时候。
- gsub函数:作用如sub,但它在整个文档中进行匹配。格式如下:
gsub (regular expression, substitution string)
gsub (regular expression, substitution string, target string)
实例:
$ awk '{ gsub(/test/, "mytest"); print }' testfile
$ awk '{ gsub(/test/, "mytest"), $1 }; print }' testfile
第一个例子在整个文档中匹配test,匹配的都被替换成mytest。
第二个例子在整个文档的第一个域中匹配,所有匹配的都被替换成mytest。
- index函数:返回子字符串第一次被匹配的位置,偏移量从位置1开始。格式如下:
index(string, substring)
实例:
$ awk '{ print index("test", "mytest") }' testfile
实例返回test在mytest的位置,结果应该是3。
- length函数:返回记录的字符数。格式如下:
length( string )
length
实例:
$ awk '{ print length( "test" ) }'
$ awk '{ print length }' testfile
第一个实例返回test字符串的长度。
第二个实例返回testfile文件中第条记录的字符数。
- substr函数:返回从位置1开始的子字符串,如果指定长度超过实际长度,就返回整个字符串。格式如下:
substr( string, starting position )
substr( string, starting position, length of string )
实例:
$ awk '{ print substr( "hello world", 7,11 ) }'
上例截取了world子字符串。
- match函数:返回在字符串中正则表达式位置的索引,如果找不到指定的正则表达式则返回0。match函数会设置内建变量RSTART为字符串中子字符串的开始位置,RLENGTH为到子字符串末尾的字符个数。substr可利用这些变量来截取字符串。函数格式如下:
match( string, regular expression )
实例:
$ awk '{start=match("this is a test",/[a-z]+$/); print start}'
$ awk '{start=match("this is a test",/[a-z]+$/); print start, RSTART, RLENGTH }'
第一个实例打印以连续小写字符结尾的开始位置,这里是11。
第二个实例还打印RSTART和RLENGTH变量,这里是11(start),11(RSTART),4(RLENGTH)。
- toupper和tolower函数:可用于字符串大小间的转换,该功能只在gawk中有效。格式如下:
toupper( string )
tolower( string )
实例:
$ awk '{ print toupper("test"), tolower("TEST") }'
- split函数:可按给定的分隔符把字符串分割为一个数组。如果分隔符没提供,则按当前FS值进行分割。格式如下:
split( string, array, field separator )
split( string, array )
实例:
$ awk '{ split( "20:18:00", time, ":" ); print time[2] }'
上例把时间按冒号分割到time数组内,并显示第二个数组元素18。
- 时间函数
- systime
- mktime(datespec)
- strftime([format [, timestamp[, utc-flag]]])
systime函数返回从1970年1月1日开始到当前时间(不计闰年)的整秒数。格式如下:
systime()
实例:$ awk '{ now = systime(); print now }'
strftime函数使用C库中的strftime函数格式化时间。格式如下:
systime( [format specification][,timestamp] )
格式 | 描述 |
%a | 星期几的缩写(Sun) |
%A | 星期几的完整写法(Sunday) |
%b | 月名的缩写(Oct) |
%B | 月名的完整写法(October) |
%c | 本地日期和时间 |
%d | 十进制日期 |
%D | 日期 08/20/99 |
%e | 日期,如果只有一位会补上一个空格 |
%H | 用十进制表示24小时格式的小时 |
%I | 用十进制表示12小时格式的小时 |
%j | 从1月1日起一年中的第几天 |
%m | 十进制表示的月份 |
%M | 十进制表示的分钟 |
%p | 12小时表示法(AM/PM) |
%S | 十进制表示的秒 |
%U | 十进制表示的一年中的第几个星期(星期天作为一个星期的开始) |
%w | 十进制表示的星期几(星期天是0) |
%W | 十进制表示的一年中的第几个星期(星期一作为一个星期的开始) |
%x | 重新设置本地日期(08/20/99) |
%X | 重新设置本地时间(12:00:00) |
%y | 两位数字表示的年(99) |
%Y | 当前月份 |
%Z | 时区(PDT) |
%% | 百分号(%) |
日期和时间格式说明符
格式 | 描述 |
%a | 星期几的缩写(Sun) |
%A | 星期几的完整写法(Sunday) |
%b | 月名的缩写(Oct) |
%B | 月名的完整写法(October) |
%c | 本地日期和时间 |
%d | 十进制日期 |
%D | 日期 08/20/99 |
%e | 日期,如果只有一位会补上一个空格 |
%H | 用十进制表示24小时格式的小时 |
%I | 用十进制表示12小时格式的小时 |
%j | 从1月1日起一年中的第几天 |
%m | 十进制表示的月份 |
%M | 十进制表示的分钟 |
%p | 12小时表示法(AM/PM) |
%S | 十进制表示的秒 |
%U | 十进制表示的一年中的第几个星期(星期天作为一个星期的开始) |
%w | 十进制表示的星期几(星期天是0) |
%W | 十进制表示的一年中的第几个星期(星期一作为一个星期的开始) |
%x | 重新设置本地日期(08/20/99) |
%X | 重新设置本地时间(12:00:00) |
%y | 两位数字表示的年(99) |
%Y | 当前月份 |
%Z | 时区(PDT) |
%% | 百分号(%) |
实例:
$ awk '{ now=strftime( "%D", systime() ); print now }'
$ awk '{ now=strftime("%m/%d/%y"); print now }'
8.2 内建数学函数
函数名称 | 返回值 |
atan2(x,y) | y,x范围内的余切 |
cos(x) | 余弦函数 |
exp(x) | 求幂 |
int(x) | 取整 |
log(x) | 自然对数 |
rand() | 随机数 |
sin(x) | 正弦 |
sqrt(x) | 平方根 |
srand(x) | x是rand()函数的种子 |
int(x) | 取整,过程没有舍入 |
rand() | 产生一个大于等于0而小于1的随机数 |
8.3 自定义函数
在awk中还可自定义函数,格式如下:
function name ( parameter, parameter, parameter, ... ) {
statements
return expression # the return statement and expression are optional
}
9 参考