分类: LINUX
2009-08-08 11:08:05
awk:倾向于处理一行中分成数个”字段”,默认是以空格或是[Tab]来分开
可以处理后续文件(文件必须格式化)也可以读取来自前一个命令的标准输出来读取数据。
命令格式:awk [-F field-saparator] ‘条件类型1{动作1} 条件2{动作2} … …’ filename
field-saparator 是分隔符,默认是以空格来作为域的分隔符
如果未设置- F选项,a w k假定空格为域分隔符,并保持这个设置直到发现一新行,但如果设置了- F选项,则a w k每次读一条记录或一行,并使用指定的分隔符分隔指定域
实际动作在大括号{ }内指明,条件在圆括号是指定。
awk是以”行”为单位来处理数据,并以”字段”来划分每行数据来对应本应的域标识,a w k执行时,其浏览域标记为$ 1,$ 2 . . . $ n,第一栏是$1,第二栏$2 … …,$0代表整行的数据
注意在打印各个域时要用逗号(,)隔开,比如:awk ‘{print $1,$3}’ filename,这样打印出来的各个域之间是以一个空格来分隔,如果打印多个域时不加分隔符,那么打印的域的数据就连接在一起。
运行awk命令出现错误信息时可相应查找:
• 确保整个a w k命令用单引号括起来。
• 确保命令内所有引号成对出现。
• 确保用大括号括起动作语句,awk是通过大括号来解释语法,用圆括号括起条件语句。
语法错误会在相应的语法下边用”^”符号标示出来,并有相应的错误提示。
使用awk时要确认数据是否有连续性,awk的输入数据大多数是一个文件或者是通过管道把前一个命令的标准输出作为自己的标准输入。
如果一行中的数据没有连续性,那格式化出来的数据可能不是我们想要的结果
awk中常用的屏蔽序列
\n 新行
\r 回车键
\t Tab键
一、抽取域,打印单独的记录
Ex:通过last把登陆帐号和对应 IP列出来,逗号(,)是以一个空格来划分各个域,可以通过用[Tab]键来分开
#last | awk '{print $1 "\t" $3}'
root 192.168.0.88
reboot boot ------------误判
#awk -F: '{print $1,$3}' passwd
printf函数常用的几个修饰符用于格式化输出
% c A S C I I字符
% d 整数
% f 浮点数
% s 字符串
# awk -F: '{printf "%-10s %-10s\n",$1,$3}' passwd
二、保存awk的输出
#awk ‘{print $0}’ passwd>passwd.new
# awk -F: '{print $1"\t"$3}' passwd > passwd.awk /*不会在屏幕上显示数据*/
# awk -F: '{print $1"\t"$3}' passwd |tee passwd.awk /*保存数据的同时还显示在屏幕上*/
三、打印报头和尾部信息
使用B E G I N语句设置计数和打印头。B E G I N语句使用在任何文本浏览动作之前,之后
文本浏览动作依据输入文件开始执行。E N D语句用来在a w k完成文本浏览动作后打印输出文本总数和结尾状态标志。如果不特别指明模式, a w k总是匹配或打印行数。
在第一行打印数据格式,之后的数据和第一行的数据格式相对应
#awk -F: 'BEGIN{printf "%-10s %-10s","Name","UID""\n-------------------------------"}{printf "%-10s %-10s\n",$1,$3}' passwd
在最后一行打印信息的结束提示语句
#awk -F: 'BEGIN{printf "%-10s %-10s\n","Name","UID""\n-------------------------------"}{printf "%-10s %-10s\n",$1,$3}END{print "---THE-END---"}' passwd
四、awk中正则表达式及其操作
正则表达式的元字符,在awk语句中使用正则表达式时要用转义符(\)把元字符转义
\ ^ $ . [] | () * + ? {} /*前边七个是一定要转义,后边四个可以不转*/
#awk -F: '($5~/\$/){print $1"\t"$5}' passwd
awk的逻辑运算字符(条件):
1)匹配模式
~ 匹配
!~ 不匹配
# awk '/cdnunion/' passwd
# awk -F: '$1~/ cdnunion /' passwd /*只匹配第一个域为cdnunion才打印*/
对文件passwd中的第七个字段匹配bash关健字的才打印
# awk -F: '{if($7~/bash/) print $1"\t"$7}' passwd
对文件passwd中的第七个字段不匹配nologin关健字的才打印
#awk -F: '{if($7!~/nologin/) print $1"\t"$7}' passwd
对大小写匹配
#awk -F: '$1~/ [Cc]dnunion/' passwd
# awk -F: '{if($1~/[Cc]dnunion/) print $1"\t"$3}' passwd /*匹配则打印第一、三列*/
或的关系匹配(|)
#awk -F: '$1~/(root| cdnunion)/' passwd
# awk -F: '{if($1~/(root|cdnunion)/) print $1"\t"$3}' passwd
匹配任意字符(.)
以c开头,后边接着6个任意字符最后是以n结尾
#awk -F: '{if($1~/^c......n/) print $1"\t"$3}' passwd
2)比较的匹配模式
> 大于
< 小于
>= 大于或等于
<= 小于或等于
== 等于
!= 不等于
要匹配第三个域大于500的数据,并列出帐号和第三个域
#awk -F: '($3>500){print $1"\t"$3}' passwd
# awk -F: '{if($3>500) print$1"\t"$3}' passwd
# awk -F: '{if($3>500) printf "%-10s %-10d\n",$1,$3}' passwd
域值比较操作
有两种方式测试一数值域是否小于另一数值域。
1) 在B E G I N中给变量名赋值。
2) 在关系操作中使用实际数值。
通常在B E G I N部分赋值是很有益的,可以在a w k表达式进行改动时减少很多麻烦。使用关系操作必须用圆括号括起来。
# awk -F: '{if($3>500)print $0}' passwd /*直接以数值来代入*/
#awk -F: 'BEGIN{CDN=500}{if($3>CDN) print $0}' passwd /*在BEGIN部分对CDN赋值*/
#awk -F:
'BEGIN{CDN=500;AGE=600}{if($3>CDN&&$3
向一行a w k命令传值
在a w k执行前将值传入a w k变量,需要将变量放在命令行中,格式如下:
awk 命令变量=输入文件值
#awk -F:
'{if($3>AGE&&$3
和在BEGIN时就给变量赋值是一样的
# awk -F:
'BEGIN{CDN=600;AGE=500}{if($3>AGE&&$3
精确匹配(==)
#awk -F: '$3==”501”{ print $1"\t"$3}' passwd /*会把第三个域匹配501字符的全列出来*/
# awk -F: '{if($3=="501") print $1"\t"$3}' passwd /*只匹配第三个域为501字符的域*/
精确不匹配(!=)
#awk -F: '{if($3!="500") print $0}' passwd
域之间的比较
#awk -F: '{if($3>$4) print $1"\t"$3,$4}' passwd
复合逻辑模式
&& and : 语句两边必须同时匹配为真。
|| or:语句两边同时或其中一边匹配为真。
多个条件匹配
#awk -F: '($3>500)&&($3<600) {print $1"\t"$3}' passwd
第一个域是cdnunion字符串,并且第三个域是500
#awk -F: '{if($1=="cdnunion" && $3==500) print $1"\t"$3}' passwd
第三个域的值是501或者是502
#awk -F: '($3==501)||($3==502){print $1"\t"$3}' passwd
第一个域是cdnunion字符串,并且第三个域是500或者501
# awk -F: '{if($1=="cdnunion" && $3~/(501|502)/) print $1"\t"$3}' passwd
awk常用的几个内置变量:
NF 每一行($0)拥有的的字段总数(默认),如果$NF就会列出最后个字段的数据出来
NR 当前awk所处理的是”第几行”数据
FS 当前的分隔符,默认是空格键,$FS列出完整的一行数据
FILENAME awk浏览的文件名,$FILENAME列出完整的一行数据
# awk -F: '{print $1 "\tlines:" NR "\tcolumes:"NF} END {print FILENAME"\n" NR "\n" FS}' passwd | tail
NF是表示各行的域的个数,默认是打印域的总数,前边加上前置”$”变量符时就只打印最后一个域的名称
#echo "/home/cdnunion/cdnunion/cdn.txt" | awk -F/ '{print NF}' /*5个域名*/
#echo "/home/cdnunion/cdnunion/cdn.txt" | awk -F/ '{print $NF}' /*第五个域名cdn.txt*/
算术操作符
+ 加
- 减
* 乘
/ 除
% 模运算
^ 幂运算
设置输入域到域变量名
可以要awk中默认设置的域名换成有意义的自定义域名,在进行模式匹配或关系操作时更容易理解,一般的变量名设置方式为n a m e = $ n,这里n a m e为调用的域变量名, n为 实际域号。
在设置了域变量名是后边一定要加一个分号(;),分号的作用是分隔a w k命令
# awk -F: '{(user=$1)(uid=$3)}{print user,uid}' passwd
#awk -F: '{user=$1;uid=$3;if(uid>500&&uid<600) print user"\t"uid}' passwd
修改数值域的取值
当在a w k中修改任何域时,要注意实际输入文件是不可修改的,修改的只是保存在缓存里的a w k复本
# awk -F: '{if($1=="cdnunion") $3=$3+8;print $1"\t"$3}' passwd /*显示了修改的值的同时又显示整个文本的内容*/
# awk -F: '{if($1=="cdnunion") {$3=$3+8;print $1"\t"$3}}' passwd/*只显示修改后的记录*/
# awk -F: '($1~/cdnunion/){$3=$3+8;print $1"\t"$3}' passwd
#awk -F: '($1=="cdnunion") {$3=$3+8;print $1"\t"$3}' passwd
修改文本域
修改文本域即对其重新赋值一个新的字符串,操作的同时字符串要使用双引号(“”),并用圆括号括起整个语法。
# awk -F: '{if($1=="cdnunion")($1="cdn");print $1}' passwd /*显示所有*/
#awk -F: '{if($1=="cdnunion"){($1="cdn");print $1}}' passwd /*只显示修改的域*/
# awk -F: '($1=="cdnunion"){($1="cdn");print $1"\t"$3}' passwd
创建新的输出域
如果第三个域小于第四个域,那么新建域八来求它们的差值
# awk -F: '{if($3<$4) {$8=$4-$3;print $1"\t"$3"\t"$4"\t"$8}}' passwd
# awk -F: '($3<$4){$8=$4-$3;print $1"\t"$8}' passwd
增加列值
在最后添加一列来对以上数据进行统计求和,同时显示所有数据
#tail passwd | awk -F: '{(tot1+=$3)(tot2+=$4)};{print $1"\t"$3"\t"$4}END{print "Total:""\t\t"tot1"\t"tot2}'
下边命令只显示最后一行的结果
# tail passwd | awk -F: '{(tot1+=$3)(tot2+=$4)}END{print "Total:""\t\t"tot1"\t"tot2}'
统计数据
有张产品的销售表:goods.txt
Name 1st 2nd 3th
goods1 125 173 208
goods2 204 185 233
goods3 356 288 253
计算每件产品的销售总额:
# awk 'NR==1{printf"%10s %10s %10s %10s %10s\n",$1,$2,$3,$4,"Total"} NR>=2{total=$2+$3+$4; printf "%10s %10d %10d %10d %10.2f\n",$1,$2,$3,$4,total}' goods.txt
对每个月的所有商品的销售做统计
#awk '{print $0}NR>=2{(tot1+=$2)(tot2+=$3)(tot3+=$4)}END{print "Total:""\t"tot1"\t"tot2"\t"tot3}' goods.txt
#awk '{print $0}' goods.txt |awk ' NR>=2(tot1+=$2)(tot2+=$3)(tot3+=$4);END{print "total:""\t"tot1"\t"tot2"\t"tot3}'
两者结合
#awk 'NR==1{printf"%10s %10s %10s %10s %10s\n",$1,$2,$3,$4,"Total"} NR>=2{total=$2+$3+$4; printf "%10s %10d %10d %10d %10.2f\n",$1,$2,$3,$4,total}NR>=2{(tot1+=$2)(tot2+=$3)(tot3+=$4)(tot4+=$5)}END{printf "%10s %10d %10d %10d %10.2f\n","Total:",tot1,tot2,tot3,tot4}' goods.txt
文件长度相加
统计当前目录下所有文件的大小,目录除外
# ls -l | awk '/^[^d]/ {print $1,$5} {tot+=$5} END {print "Total:""\t"tot" KB"}'