awk,sed,grep俗称linux下的三剑客。
awk是一门模式匹配的编程语言,因为它的主要功能是用于匹配文本并处理,同时它有一些编程语言才有的语法,例如函数、分支循环语句、变量等,当然比起我们常见的额编程语言,awk相对简单。
使用awk我们可以做以下事情:
- 将文本文件视为由字段和记录组成的文本数据库;
- 在操作文本数据库的过程中使用变量;
- 能够使用数学运算和字符串操作;
- 能够使用常见的编程结构,例如条件分支和循环;
- 能够格式化输出;
- 能够自定义函数;
- 能够在awk脚本中执行UNIX命令;
- 能够处理UNIX命令的输出结果;
命令行语法:
- awk [-F ERE] [-v assignment] ... program [argument]
- awk [-F ERE] -f profile ... [-v assignment] ... [argument]
说明:awk的脚本可以写到一个文件中,通过-f参数指定,可以同时指定多个脚本,它们会按照在命令行中出现的顺序连接在一起。program一般由多个pattern和action序列组成,当读入的记录匹配pattern时,才会执行相应的action命令。-F参数定义字段分隔符。-v定义awk变量,形式同awk中的变量赋值,即name = value,赋值发生在awk处理文本之前。
awk的输入被解析成多个记录(Record),默认情况下记录的分隔符是\n,因此可以认为一行就是一个记录,记录的分隔符可以通过内置变量
RS更改,如果将RS赋值为空,那么连续不为空行的所有行为一个记录,并且强制字段分隔符为回车(FS不能被赋值)。每个记录被进一步地分隔成多个字段(Field),默认情况下字段的分隔符是空白符,也可以通过-F ERE选项或者内置变量
FS更改。可以通过$1,$2...来访问对应位置的字段,同时$0存放整个记录。
内置变量NF记录着字段的个数,所以$NF表示最后一个字段。
举几个简单例子:
input:
echo "1:2:3" | awk -F : '{print $1 " and " $2 " and " $3}'
output:
1 and 2 and 3
input:
echo | awk -v a = 1 'BEGIN {print a}'
output:
1
从上面可以看到,通过-v设置的变量在BEGIN的位置就可以访问了。BEGIN是一个特殊的pattern,它在awk处理输入之前就会执行,可以认为是一个初始化语句,与此对应的还有END。
argument有两种形式,分别是输入文件和变量赋值。
可以指定多个输入文件,如果输入文件的文件名为'-',表示从标准输入读取内容。
变量赋值必须位于脚本参数的后面,与文件名参数无先后顺序的要求,但是位于不同位置的赋值它的执行时机不同。
三个简单例子:
有两个文本文件:a和b。
input:
awk 'BEGIN{print "BEGIN: " var}{print "PROCESS: " var}END{print "END: " var}'
var = 1 a
output:
BEGIN:
PROCESS: 1
END: 1
input:
awk 'BEGIN{print "BEGIN: " var}{print "PROCESS: " var}END{print "END: " var}' a var = 1
output:
BEGIN:
PROCESS:
END: 1
input:
awk 'BEGIN{print "BEGIN: " var}{print "PROCESS: " var}END{print "END: " var}' a var = 1 b
output:
BEGIN:
PROCESS:
PROCESS: 1
END: 1
在脚本中可以自定义函数,function name(parameter list){ statements },参数默认是局部变量无法在函数之外访问,而在函数中定义的变量为全局变量,可以在函数之外访问。
模式(Pattern)有以下几种情况:
- /regular expression/:扩展的正则表达式;
- relational expression:关系表达式,例如大于小于等于,关系表达式结果为true表示匹配;
- BEGIN:特殊的模式,在第一个记录处理之前被执行,常用于初始化语句的执行;
- END:特殊的模式,在最后一个记录处理之后被执行,常用于输出汇总信息;
- pattern,patterm:模式对,匹配两者之间的所有记录;
例子:输出匹配3的字段
input:
seq 1 20 | awk '/3/ {print}'
output:
3
13
相反,可以在正则表达式之前加上'!',表示不匹配,例如:
input:seq 1 5 | awk '!/3/ {print}'
output:
1
2
4
5
除了BEGIN和END这两个特殊模式,其余模式都可以使用'&&'或者'||'。
前面的匹配都是整行匹配,有时候仅仅需要匹配某个字符,这样我们可以用表达式$n ~ /ERE/,例如:
input:
awk '$1 ~ /ser/ {print}' /etc/passwd
output:
username:x:1000:1000::/home/kodango:/bin/bash
NR表示要显示的行数(可赋值),例如:
input:
seq 1 5 | awk 'NR == 1 {print}'
output:
1
数组在awk中下标可以为数字或者字符串;可以用for(item in array)遍历,
item是下标;也可以在if(item in array)中进行判断。
变量名
|
描述
|
ARGC
|
命令行参数的个数,即ARGV数组的长度
|
ARGV
|
存放命令行参数
|
CONVFMT
|
定义awk内部数值转换成字符串的格式,默认值“%.6g”
|
OFMT
|
定义输出时数值转换成字符串的格式,默认值为“%.6g”
|
ENVIRON
|
存放系统环境变量的关联数组
|
FILENAME
|
当前被处理的文件名
|
NR
|
记录的总个数
|
FNR
|
当前文件中记录的总个数
|
FS
|
字段分隔符,默认为空白
|
NF
|
每个记录中的字段的个数
|
RS
|
记录的分隔符,默认为回车
|
RLENGTH
|
被match函数匹配的子串长度
|
RSTART
|
被match函数匹配的子串位于目标字符串的起始下标
|
说明:
ARGV存放命令行参数,并且排除命令行选项(例如-v/-f),因此事实上ARGV只存储argument的部分,即文件名以及变量赋值两部分的内容,可以对ARGV修改,例如:
假设有a,b两个文件,它们各有一行内容:file a和file b。
input:
awk 'BEGIN{ARGV[1] = ""} {print}' a b
output:
file b
也可以删除ARGV达到以上例子的效果:
input:
awk 'BEGIN{delete ARGV[1]} {print}' a b
增加ARGV则需要先将ARGC+1,然后再增加ARGV,例:
input:
awk 'BEGIN{ARGC+=1; ARGV[1]="a"} {print}'
output:
file a
CONVFMT和OFMT:
input:
awk 'BEGIN{
printf "CONVFMT=%s, num=%f, str=%s", CONVFMT, 12.11, 12.11
}'
output:
CONVFMT=%.6g, num=12.110000, str=12.11
可以通过更改CONVFMT定义自己的转换格式:
input:
awk 'BEGIN{
CONVFMT="%d";
printf "CONVFMT=%s, num=%f, str=%s", CONVFMT, 12.11, 12.11
}'
output:
CONVFMT=%d, num=12.110000, str=12
OFMT与其类似,只不过是影响输出时候数字转换成字符串的格式:
input:
awk 'BEGIN{ OFMT="%d"; print 12.11}'
output:
12
ENVIRON是存放系统环境变量的数组。
语句:包括print,printf,delete,break,continue,exit,next。其中exit是退出awk的处理直接进入END处理。next是跳转到下一个记录并重新回到脚本的最开始处执行。print输出时,字段之间的分隔符可以由OFS重新定义。print的输出还可以重定向到某个文件或某个命令。
重定向命令的例子:
假设存在一个文件num.list,每行内容分别是,1,3,2,9,5。
input:
awk '{print | "sort -n"}' num.list
output:
1
2
3
5
9
awk的函数包括:
- 数学函数
- 字符串函数
- I/O处理函数
- 用户自定义的函数
数学函数:
awk支持以下数学函数:
- atan2(y, x)反正切函数;
- cos(x);
- sin(x);
- exp(x)以自然对数e为底的指数函数;
- log(x)计算以e为底的对数值;
- sqrt(x)绝对值;
- int(x)将数值转换为整数;
- rand()返回0到1的一个随机数值,不包含1;
- srand([expr])设置随机种子,如果参数为空,默认使用当前时间为种子;
字符串函数:
- sub(ere, repl[,in])将in中匹配ere的部分替换成repl,如果in参数省略,默认使用$0。例如:
input:
echo "hello, world" | awk '{print sub(/ello/, "i"); print}'
output:
1
hi, world
在repl参数中&是一个元字符,它表示匹配的内容,例如:
input:
awk 'BEGIN{var = "username"; sub(/username/, "hello, &", var);print var}'
output:
hello, username
- gsub(ere,repl[,in])同sub()函数功能类似,但是gsub()函数是全局替换。
- index(s,t)返回字符串t在s中出现的位置(这里位置是从1开始计算)
- length[([s])]s缺省则默认参数为$0
- match(s,ere)返回字符串s匹配ere的起始位置,不匹配返回0。该函数会定义RSTART和RLENGTH两个内置变量。RSTART和返回值相同,RLENGTH记录匹配子串的长度,如果不匹配则为-1。
- split(s,a[,fs])分隔字符串,存到数组a中,如果fs为空,则默认使用FS进行分隔。函数返回值为分隔的个数。
- sprintf(fmt,expr,expr,...)类型printf,不过不会输出而是产生返回值。
- substr(s,m[,n])返回从位置m起,长度为n的子串。
- tolower(s)。
- toupper(s)。
I/O处理函数:
- getline:
例子:设想有一个statement.txt,内容每行分别为st1,st2,st3。
input:
awk 'BEGIN{ while("cat statement.txt" | getline var) print var}'
output:
st1
st2
st3
如果getline后没有var来承接,则直接写到$0,同时NF值被更新。
也可以直接getline,例如while(getline)print $0。还可以getline [var] < expression,从expression中重定向输入。
- close:关闭已经打开的文件或者管道。
- system:用于执行外部命令。