(转)
Linux awk几乎是一种自解释的独立编程语言。它的最基本的功能就是在文件和字符串中基于指定规则浏览和抽取信息。有三种方式可以调用awk,第一种是命令行方式,例如:
- awk [-F seperator-fields] 'commands' input-file
awk [-F seperator-fields] 'commands' input-file
commands是真正的awk命令,即由一系列的大括号组成,-F 域分割符 这个是可选的,awk默认采用空格来分割,但是对于/etc/passwd这样的文件,可能就需要采用冒号: 这样的分割符号。
第二种方式是将所有的awk命令插入一个文件,并使awk程序可执行,然后用awk命令解释器作为脚本的首行,以便通过键入脚本名称来调用它
第三种方式是将所有awk命令插入一个单独的文件,然后输入以下命令来调用:
- awk -f awk-script-file input-files
- //-f 选项指明在文件awk_script_file的awk脚本,input_file是使用awk进行浏览的文件名
awk -f awk-script-file input-files
//-f 选项指明在文件awk_script_file的awk脚本,input_file是使用awk进行浏览的文件名
awk语言中采取一种特别的方式来访问文件--域和记录
awk执行的时候,将其浏览域标记为$1 $2 ... $n ,使用$1,$3表示将参考第一个域和第三个域,这里采用逗号来分隔开不同的域,如果希望打印全部的域,可以直接使用$0,而不必一个一个域的去指定。例如,打印/etc/passwd文件里的第一列(也即登录的用户名)
- awk -F : '{ print $1 }' /etc/passwd
awk -F : '{ print $1 }' /etc/passwd
将输出该文件的第一列
任何awk语句都是由模式和动作来组成,模式部分决定动作语句何时触发及触发事件,处理即对数据采取的动作,如果省略模式,动作将时刻保持执行状态。
模式可以是任意的正则表达式语句或其他条件语句,它包括两个特殊的字段,BEGIN和END,BEGIN使用在任何文本浏览动作之前,常用来打印行头,而END用在所有文本浏览动作之后,常用来打印文本总数和结尾状态标志。
例如:有这样一个文件:
- [chenwu@localhost unit9-awkIntroduce]$ cat grade.txt
- chenwu 05/99 4811
- mary 02/22 1231
- tom 09/15 1182
[chenwu@localhost unit9-awkIntroduce]$ cat grade.txt
chenwu 05/99 4811
mary 02/22 1231
tom 09/15 1182
下面的语句将打印一个常用的报表头,及第一列和第三列。
- [chenwu@localhost unit9-awkIntroduce]$ awk 'BEGIN {print "name""\t""id"} {print $1"\t"$3}' grade.txt
- name id
- chenwu 4811
- mary 1231
- tom 1182
[chenwu@localhost unit9-awkIntroduce]$ awk 'BEGIN {print "name""\t""id"} {print $1"\t"$3}' grade.txt
name id
chenwu 4811
mary 1231
tom 1182
如果加上行尾,则应该是这样:
- [chenwu@localhost unit9-awkIntroduce]$ awk 'BEGIN {print "name""\t""id"} {print $1"\t"$3} END {print "end-of-report"}'
- grade.txt >grade.report
- [chenwu@localhost unit9-awkIntroduce]$ ls
- grade.report grade.txt
- [chenwu@localhost unit9-awkIntroduce]$ cat grade.report
- name id
- chenwu 4811
- mary 1231
- tom 1182
- end-of-report
[chenwu@localhost unit9-awkIntroduce]$ awk 'BEGIN {print "name""\t""id"} {print $1"\t"$3} END {print "end-of-report"}'
grade.txt >grade.report
[chenwu@localhost unit9-awkIntroduce]$ ls
grade.report grade.txt
[chenwu@localhost unit9-awkIntroduce]$ cat grade.report
name id
chenwu 4811
mary 1231
tom 1182
end-of-report
在碰到awk错误的时候,应该这样查找错误:
1:查找命令commands是否被单引号括起来
2:查找commands内大括号是否匹配
尝试着加入条件判断:
这里打印名称为mary的行. 使用if($x(第几列)~ 正则表达式) 语句
- [chenwu@localhost unit9-awkIntroduce]$ awk 'BEGIN {print "name""\t""ID"} {if($1 ~/mary/) print $0}
- END {print "end-of-report"}' grade.txt
- name ID
- mary 02/22 1231
- end-of-report
[chenwu@localhost unit9-awkIntroduce]$ awk 'BEGIN {print "name""\t""ID"} {if($1 ~/mary/) print $0}
END {print "end-of-report"}' grade.txt
name ID
mary 02/22 1231
end-of-report
而取反的方式是if($1 !~ /mary/)
在awk中组合条件语句与javascript很类似,都是使用||或者&& ,这与传统的shell -o 或者 -a 不同。
同时在awk中提供了很多内置变量,可以方便的运用。常用的有以下几个:
NF 域的个数 NR 已读记录的个数 FILENAME表示文件名
例如:
- [chenwu@localhost unit9-awkIntroduce]$ awk '{print NF,NR,$0} END{print FILENAME}' grade.txt
- 3 1 chenwu 05/99 4811
- 3 2 mary 02/22 1231
- 3 3 tom 09/15 1182
- grade.txt
[chenwu@localhost unit9-awkIntroduce]$ awk '{print NF,NR,$0} END{print FILENAME}' grade.txt
3 1 chenwu 05/99 4811
3 2 mary 02/22 1231
3 3 tom 09/15 1182
grade.txt
awk中域值的比较操作有以下两种方式:
第一种方式,直接在条件表达式里硬编码:
例如,想查看所有得分在27分以下的同学:
- [chenwu@localhost unit9-awkIntroduce]$ cat grade.txt
- chenwu 05/99 4811 27
- mary 02/22 1231 30
- tom 09/15 1182 25
- [chenwu@localhost unit9-awkIntroduce]$ awk '{if($4<27) print $0}' grade.txt
- tom 09/15 1182 25
[chenwu@localhost unit9-awkIntroduce]$ cat grade.txt
chenwu 05/99 4811 27
mary 02/22 1231 30
tom 09/15 1182 25
[chenwu@localhost unit9-awkIntroduce]$ awk '{if($4<27) print $0}' grade.txt
tom 09/15 1182 25
这里还有第二种方式,在BEGIN模块里定义变量,从而可以提高内聚性。例如:
- [chenwu@localhost unit9-awkIntroduce]$ awk 'BEGIN {baseline=27} {if($4<baseline) print $0}' grade.txt
- tom 09/15 1182 25
[chenwu@localhost unit9-awkIntroduce]$ awk 'BEGIN {baseline=27} {if($4<baseline) print $0}' grade.txt
tom 09/15 1182 25
注意这里取变量的时候,不能加上$符号,如果不小心加上$baseline,那么什么都不会输出了:
- [chenwu@localhost unit9-awkIntroduce]$ awk 'BEGIN {baseline=27} {if($4<$baseline) print $0}' grade.txt
- [chenwu@localhost unit9-awkIntroduce]
[chenwu@localhost unit9-awkIntroduce]$ awk 'BEGIN {baseline=27} {if($4<$baseline) print $0}' grade.txt
[chenwu@localhost unit9-awkIntroduce]
如果只是想统计某一列的总和等,则应该这样做:
- [chenwu@localhost unit9-awkIntroduce]$ awk '{total+=$4} END {print "total scores are "total}' grade.txt
- total scores are 82
[chenwu@localhost unit9-awkIntroduce]$ awk '{total+=$4} END {print "total scores are "total}' grade.txt
total scores are 82
再例如,只查找所有普通文件的长度总和(非目录)。可以参考下面的方式:
- [chenwu@localhost unit9-awkIntroduce]$ ls -l | awk 'BEGIN {total=0} {if($1~/^[^d]/) total+=$5;print $0}
- END {print "total size:"total}'
- 总计 12
- -rw-rw-r-- 1 chenwu chenwu 53 06-10 10:16 grade.report
- -rw-rw-r-- 1 chenwu chenwu 64 06-11 10:12 grade.txt
- drwxrwxr-x 2 chenwu chenwu 4096 06-11 10:28 testDir
- total size:117
[chenwu@localhost unit9-awkIntroduce]$ ls -l | awk 'BEGIN {total=0} {if($1~/^[^d]/) total+=$5;print $0}
END {print "total size:"total}'
总计 12
-rw-rw-r-- 1 chenwu chenwu 53 06-10 10:16 grade.report
-rw-rw-r-- 1 chenwu chenwu 64 06-11 10:12 grade.txt
drwxrwxr-x 2 chenwu chenwu 4096 06-11 10:28 testDir
total size:117
当然也可以将if($1 ~/^[^d]/) 换成if($1 ! ~ /^d/),例如:
- [chenwu@localhost unit9-awkIntroduce]$ ls -l | awk 'BEGIN {total=0} {if($1!~/^d/) total+=$5;print $0}
- END {print "total size:"total}'
- 总计 12
- -rw-rw-r-- 1 chenwu chenwu 53 06-10 10:16 grade.report
- -rw-rw-r-- 1 chenwu chenwu 64 06-11 10:12 grade.txt
- drwxrwxr-x 2 chenwu chenwu 4096 06-11 10:28 testDir
- total size:117