awk分别代表其作者姓氏的第一个字母。
awk 基础
awk语法有两种形式:
awk [-F ERE] [-v assignment] 'program' [argument ...]
awk [-F ERE] -f progfile ... [-v assignment] ...[argument ...]
注意:第一种形式的 命令(program) 必须是用单引号引起。 awk -v a='a b c' 'BEGIN {print a; a=123} END{print a}'
标准的awk命令行参数主要由以下三个:
- -F ERE:定义字段分隔符,该选项的值可以是一个字符串或扩展的正则表达式(ERE),如:
-F:
表示以冒号分隔字段。 - -f progfile:指定awk脚本,可以同时指定多个脚本,它们会按照在命令行中出现的顺序连接在一起。
- -v assignment:定义awk变量,形式同awk中的变量赋值,即name=value,赋值发生在awk处理文本之前。
awk是一种编程语言,逐行扫描文件,寻找匹配特定模式的行,并在这些行上执行操作。
awk可以同时指定多个输入文件,如果输入文件的文件名为-
,表示从标准输入读取内容。
Awk的输入被解析成多个记录(Record),默认情况下,记录的分隔符是 \n
,因此可以认为一行就是一个记录,记录的分隔符可以通过内置变量RS更改。当记录匹配某个pattern
时,才会执行后续的action
命令。
而每个记录又进一步地被分隔成多个字段(Field),默认情况下字段的分隔符是空白符,例如空格、制表符等等,也可以通过-F ERE选项或者内置变量FS更改。在awk中,可以通过 $1,$2…
来访问对应位置的字段,同时 $0
存放整个记录,这一点有点类似shell下的命令行位置参数。
内置变量
变量名 描述
$0 当前记录(存放整行的内容)
$1-$n 当前记录的第n个字段,字段间由FS分隔
FS 输入的字段分隔符,默认为空格或Tab
NF 当前记录中字段的个数,就是有多少列
RS 输入的记录的分隔符,默认为换行符
NR 已经读出的记录数,就是行号,从1开始,如果有多个文件,这个值也是不断累加。
FNR 当前记录数,与NR不同的是,这个值是各个文件自己的行号
FILENAME 当前被处理的文件名
OFS 输出时字段的分隔符,默认为空格
ORS 输出时记录的分隔符,默认为换行符
ARGC 命令行参数的各个,即ARGV数组的长度
ARGV 存放命令行参数
CONVFMT 定义awk内部数值转换成字符串的格式,默认值为”%.6g”
OFMT 定义输出时数值转换成字符串的格式,默认值为”%.6g”
ENVIRON 存放系统环境变量的关联数组
RLENGTH 被match函数匹配的子串长度
RSTART 被match函数匹配的子串位于目标字符串的起始下标
模式和操作
awk脚本是由模式和操作组成的:pattern {action}
。
两者是可选的,如果没有模式,则action应用到全部记录,如果没有action,则输出匹配的全部记录。
默认情况下,每一个输入行都是一条记录,但用户可通过RS变量指定不同的分隔符进行分割。
模式
可以是一下任意一个:
模式 说明
/正则表达式/ 使用通配符的扩展集。如: /abc/ 表示匹配含有abc的记录 。
关系表达式 用关系运算符进行操作,可以是字符串或数字的比较,如:$2 > %1 表示选择第二个字段比第1个字段大的行。
模式匹配表达式 用运算符~ 匹配 和~! 不匹配 模式。如: $6 ~ /abc/ ,表示匹配第6个字段包含abc的记录。
模式, 模式 指定一个行的范围。该语法不能包括BEGIN和END模式。
BEGIN 让用户指定在第一条输入记录被处理之前所发生的动作,通常可在这里设置全局变量。如: awk 'BEGIN {FS=":"}'
END 让用户在最后一条记录被读取之后发生的动作。如: echo -e "1\n2\n3\n12" | awk '{sum += $1} END {print sum}'
操作
由一个或多个命令、函数、表达式组成,之间由换行符或分号隔开,并位于大括号内。主要有四部分:
- 变量或数组赋值, a[i]++ 表示对数组a的第i个元素进行加1。
- 输出命令
print 12 " = " 456
将输出12 = 456
. - 内置函数
- 控制流命令 可以使用
if else if else
语句
完整的awk命令可以是这样的:
awk ' BEGIN {处理文件前执行的命令} {对每一行进行处理的命令} END {处理完文件后执行的命令} ' files
运算符、表达式、变量
运算符
运算符 描述
= += -= *= /= %= ^= **= 赋值
?: C条件表达式
|| 逻辑或
&& 逻辑与
~ ~! 匹配正则表达式和不匹配正则表达式
< <= > >= != == 关系运算符
空格 连接
+ - 加,减
* / & 乘,除与求余
+ - ! 一元加,减和逻辑非
^ *** 求幂
++ -- 增加或减少,作为前缀或后缀
$ 字段引用
in 数组成员
表达式(Expressions)
表达式可以由常量、变量、运算符和函数组成,常数和变量的值可以为字符串和数值。
变量
内建变量一般是大写的,参考前面“内置变量”。
用户定义变量可以是字符串或数字,不需要预先声明或初始化,未初始化的字符串变量的值为""
,未初始化的数值变量的值为0
。
字段变量可以用$n来引用,n的取值范围为[0,NF],NF是当前行的记录总数。n可以为一个变量,例如$NF代码最后一个字段,而$(NF-1)表示倒数第二个字段。字段变量可被修改和赋值。
awk还可以环境变量,通过使用 -v 参数和 ENVIRON ,使用ENVIRON 的环境变量需要先 export。
export y=12
echo "" | awk -v var="abc" 'END{print var, ENVIRON["y"]}'
正则元字符
通用的元字符集,与sed一样。
^ 表示行的开头。
$ 表示行的结尾。
. 句点表示任意单个字符
* 星号表示某个字符出现了0次或多次。
[] 字符集合。如[0-9]表示数字0-9中的任意一个,[a-zA-Z]表示字母中的任意一个。如果字符集以^开头表示非,如[^0-9]表示非数字。
gawk专用元字符集,不适合unix版本的awk。
\Y 匹配一个单词的开头或者末尾的空字符串。
\B 匹配单词内的空字符串。
\< 表示词首,如 \ 表示词尾,如 abc\>表示以abc为尾的单词。
\w 匹配一个字母数字组成的单词。
\W 匹配一个非字母数字组成的单词。
\' 匹配字符串开头的一个空字符串。
\' 匹配字符串末尾的一个空字符串。
例子
使用运算符过滤记录
awk '$3 == 0 && $6 != "LISTEN" {printf "%-20s %-10s\n", $5, $6} ' netstat.txt
指定分隔符
下面两句是等价的:
awk 'BEGIN{ FS=":" } {print $1, $3, $6}' /etc/passwd
awk -F: '{print $1, $3, $6}' /etc/passwd
指定输出分隔符,必须在命令之后指定,之前指定会出错:
awk -F: '{print $1,$3,$6}' OFS="\t" /etc/passwd
使用变量和END模式
awk里的变量使用前不需要先定义,可以在 BEGIN 语句块里初始化。
echo -e "1\n2\n3\n12" | awk '{sum += $1} END {print sum}'
字符串匹配
awk '$6 ~ /FIN|TIME/ || NR == 1 {print NR,$4,$5,$6}' OFS="\t" netstat.txt
/FIN|TIME/
表示匹配含有 FIN或 TIME。
字符串不匹配
awk '$6 !~ /FIN/ || NR == 1 {print NR,$4,$5,$6}' OFS="\t" netstat.txt
拆分文件
awk 拆分文件可以使用重定向(>, >>)。有列值分区的感觉。
可以按某一列的值来拆分文件,以第N列为拆分列,其值为A、B、C三个值之一,那么所有第N列值为A的都行都被分拆到文件名为A的文件中,所有第N列值为B的都行都被分拆到文件名为B的文件中,所有第N列值为C的都行都被分拆到文件名为C的文件中。
awk 'NR!=1 {print > $6} ' netstat.txt
也可以选定输出列:
awk 'NR!=1 {print $3, $4, $5 > $6} ' netstat.txt
输出到指定文件
awk 'NR!=1 {if ($6 ~ /TIME|ESTABLISHED/) print > "1.txt"; else if ($6 ~ /LISTEN/) print > "2.txt"; else print > "3.txt" }' netstat.txt
awk 编程
重定向和管道
- awk可以使用shell的重定向符进行输出重定向。如:
awk 'NR!=1 {if ($6 ~ /TIME|ESTABLISHED/) print > "1.txt"; else if ($6 ~ /LISTEN/) print > "2.txt"; else print > "3.txt" }' netstat.txt
。 -
输入重定向需用到getline函数。getline从标准输入、管道或者当前正在处理的文件之外的其他输入文件获得输入。 它负责从输入获得下一行的内容,并给NF, NR, FNR等内置变量赋值。如果得到一条记录,getline函数返回1,如果到达文件的末尾就返回0,出现错误就返回-1。
awk 'BEGIN {"date" | getline d; print d}'
, 得到输出 : Tue Apr 16 15:21:13 2013
这里不需要指定输入文件,因为BEGIN块在打开输入文件前执行,所以可以忽略输入文件。awk 'BEGIN{printf "What is your name ?"; getline name < "/dev/tty"} $1 ~ name {print "Found " name " on line " NR "."} END{print " See you " name "."}' namefile
这个例子从终端读入一行赋值给name,以name的值作为正则表达式来匹配。awk 'BEGIN{lc=0; while (getline < "/etc/passwd" > 0) lc++; print lc}'
从文件读取输入。 -
可以在awk中打开一个管道,且同一时刻只能由一个管道存在。通过
close()
可关闭管道。如:awk '{print $1 | "sort" } END{close("sort")}' namefile
,把print的输出作为sort的输入,最后在END
块执行关闭管道的操作。 -
system
函数可以在awk中执行linxu的命令。如:awk 'BEGIN{system("clear")}'
-
fflush
函数可以刷新输出缓冲区,如果没有参数,就刷新标准输出的缓冲区,如果以空字符串为参数,如fflush("")
,则刷新所有文件和管道的输出缓冲区。
if else if else 语句
awk中的if与c的类似:
if (expression) {
statements;
}
if (expression) {
statements;
} else {
statements;
}
if (expression) {
statements;
} else if (expression) {
statements;
} else {
statements;
}
while、for循环
- 这两个循环与C语言的还是很像的,但for循环支持在数组上进行for-each迭代。
while(condition) {
statements;
}
for (init-statement; condition; step-statment) {
statements;
}
for (name/index in array) {
statements;
}
- breadkcontinue语句。break用于在满足条件的情况下跳出循环;continue用于在满足条件的情况下忽略后面的语句,直接返回循环的顶端。
awk 'BEGIN{c=5; for(i=0; i < c; i++) if (i==2) {continue;} else if (i==3){break;}else{print i} }' namefile
-
next
语句从输入文件中读取一行,然后从头开始执行awk脚本。
awk '{if ($1 ~ /bruce/){next} else {print $1 | "sort" }} END{close("sort")}' namefile
-
exit
语句用于结束awk程序,但不会略过END块。退出状态为0代表成功,非0表示出错。
数组
awk中的数组下标可以是数字和字母,称为关联数组。
- 使用变量做数组下标:
awk '{name[x++]=$1;} END{for (i=0;i<NR;i++) print i, name[i]}' namefile
。 -
使用for-each循环
for (item in arrayname){
print arrayname[item]
}
-
用字符串作为下标。如:count[“test”]。
-
delete函数用于删除数组元素。如:$ awk ‘{line[x++]=$1} END{for(x in line) delete(line[x])}’ test。分配给数组line的是第一个域的值,所有记录处理完成后,for-each循环将删除每一个元素。
一个完整的例子如下所示:
<
pre>
echo "1 2 3" | awk '{
for (i=0;i<NF;i++)
a[i]=i;
}
END {
print 3 in a
for (i in a)
printf "%s: %s\n", i, a[i];
}'
还可以在if
分支判断中使用in
操作符: if (item in array)
。
函数
自定义函数
函数定义格式如下所示:function name(parameter list) { statements }
函数的参数列表用逗号分隔,参数默认是局部变量,无法在函数之外访问,而在函数中定义的变量为全局变量,可以在函数之外访问。
Awk脚本中的语句使用空行或者分号分隔,使用分号可以放在同一行,不过有时候会影响可读性,尤其是分支或循环结构中,很容易出错。
自定义函数的返回语句是可选的。
数学函数
awk中支持以下数学函数:
atan2(y,x):反正切函数;
cos(x):余弦函数;
sin(x):正弦函数;
exp(x):以自然对数e为底指数函数;
log(x):计算以e 为底的对数值;
sqrt(x):绝对值函数;
int(x):将数值转换成整数;
rand():返回0到1的一个随机数值,不包含1;
srand([expr]):设置随机种子,一般与rand函数配合使用,如果参数为空,默认使用当前时间为种子;
字符串函数
sub(ere, repl[, in])
将in中匹配ere的部分替换成repl,返回值是替换的次数。如果in参数省略,默认使用$0。替换的动作会直接修改变量的值。在repl参数中&是一个元字符,它表示匹配的内容
gsub(ere, repl[, in])
同sub()函数功能类似,只不过是gsub()是全局替换,即替换所有匹配的内容。
index(s, t)
返回字符串t在s中出现的位置,注意这里位置是从1开始计算的,如果没有找到则返回0。
length[([s])]
返回字符串的长度,如果参数s没有指定,则默认使用$0作为参数。
match(s, ere)
返回字符串s匹配ere的起始位置,如果不匹配则返回0。该函数会定义RSTART和RLENGTH两个内置变量。RSTART与返回值相同,RLENGTH记录匹配子串的长度,如果不匹配则为-1。
split(s, a[, fs])
将字符串按照分隔符fs,分隔成多个部分,并存到数组a中。注意,存放的位置是从第1个数组元素开始的。如果fs为空,则默认使用FS分隔。函数返回值分隔的个数。
sprintf(fmt, expr, expr, …)
类似printf,只不过不会将格式化后的内容输出到标准输出,而是当作返回值返回。
substr(s, m[, n])
返回从位置m开始的,长度为n的子串,其中位置从1开始计算,如果未指定n或者n值大于剩余的字符个数,则子串一直到字符串末尾为止。
tolower(s)
将字符串转换成小写字符。
toupper(s)
将字符串转换成大写字符。
I/O处理函数
getline
getline的用法相对比较复杂,它有几种不同的形式。不过它的主要作用就是从输入中每次获取一行输入。
- expression | getline [var]
这种形式将前面管道前命令输出的结果作为getline的输入,每次读取一行。如果后面跟有var,则将读取的内容保存到var变量中,否则会重新设置$0和NF。
例如,我们将上面的statement.txt文件的内容显示作为getline的输入:
[kodango@devops awk_temp]$ awk ‘BEGIN { while(“cat statement.txt” | getline var) print var}’
statement
delete
exit
next
如果不加var,则直接写到$0中,注意NF值也会被更新。
- getline [var]
第二种形式是直接使用getline,它会从处理的文件中读取输入。同样地,如果var没有,则会设置$0,并且这时候会更新NF, NR和FNR:
[kodango@devops awk_temp]$ awk ‘{while (getline)
print NF, NR, FNR, $0;
}’ statement.txt
1 2 2 delete
1 3 3 exit
1 4 4 next - getline [var] < expression
第三种形式从expression中重定向输入,与第一种方法类似,这里就不加赘述了。
close
close函数可以用于关闭已经打开的文件或者管道,例如getline函数的第一种形式用到管道,我们可以用close函数把这个管道关闭,close函数的参数与管道的命令一致:
[kodango@devops awk_temp]$ awk ‘BEGIN {
while(“cat statement.txt” | getline) {
print $0;
close(“cat statement.txt”);
}}’
但是每次读了一行后,关闭管道,然后重新打开又重新读取第一行就死循环了。所以要慎用,一般情况下也很少会用到close函数。
system
这个函数很简单,就是用于执行外部命令,例如:
$ awk ‘BEGIN {system(“uname -r”);}’
参考资料
- awk 简明教程: http://coolshell.cn/articles/9070.html
- Awk学习笔记: http://man.lupaworld.com/content/manage/ringkee/awk.htm
- 《sed & awk》读书笔记: http://blog.jobbole.com/31817
- gawk 手册: http://www.gnu.org/software/gawk/manual/gawk
欢迎关注我的微信公众号: coderbee笔记,可以更及时回复你的讨论。