声明:一下内容均总结自鸟哥私房菜。
sed是对行进行处理,而awk是对列做处理。看下面这个例子: 将上次登录的用户前三行列出来,只显示用户名和IP
[root@localhost ~]# last | awk '{print $1 "\t" $3}' | head -n 3
root 192.168.1.1
root 192.168.1.1
root 192.168.1.1
可以通过awk指令,只将第一列和第三列的内容取出来。awk中默认的分隔符是空格或者tab键,所以有时候取出来的数据类型并不是一致的,可能是你的数据结构有问题。 awk的指令格式通常是这样的:
[root@linux ~]# awk '条件类型 1{动作 1} 条件类型 2{动作 2} ...' filename
其中条件类型可有可无,比如像最上面这个例子,只存在指令不存在条件。注意,awk后续的所有指令都要使用’’单引号扩起来,打印时非变量的部分要使用双引号扩起来。动作必须存放在{}中,变量$1,$2,$3等就表示第一列,第二列,第三列等,而$0比较特殊,它表示一整行。
awk指令执行的顺序是下面这样的: 1. 读入第一行,将第一行存放在$0中,将第一列,第二列等分别存放在,$1, $2…. 等变数当中; 2. 依据 “条件类型” 的限制,判断是否需要进行后面的 “动作”; 3. 做完所有的动作与条件类型; 4. 若还有后续的『行』的数据,则重复上面 1~3 的步骤,直到所有的数据都读完为止。
awk中一些内建变量
变量名称 | 代表含义 |
---|---|
NF | 每一行 ($0) 拥有的字段总数 |
NR | 目前 awk 所处理的是『第几行』数据 |
FS | 目前的分隔字符,预设是空格键 |
例1:获取目前所处理的行数和该行的字段数量
[root@localhost ~]# last | awk '{print $1"\t lines: " NR "\t columns: " NF}'
例2:读取/etc/passwd中的内容,当第三列的值小于10时打印出来
[root@localhost ~]# cat /etc/passwd | awk '{FS=":"} $3<10 {print $1 "\t" $3}'
root:x:0:0:root:/root:/bin/bash
bin 1
daemon 2
adm 3
lp 4
sync 5
shutdown 6
halt 7
mail 8
在这个例子中,先使用cat /etc/passwd读取文件内容,在作为管道流送到下个指令awk ‘{FS=”:”} $3<10 {print $1 “\t” $3}’处理,在该指令中第一个动作{FS=”:”}将分隔符设置成”:”号,设置判断条件$3<10,如果第三列的值小于10,则执行动作{print $1 “\t” $3},将第一列和第三列的值输出来。但是,可以看到第一行没有正确显示出来,因为在读入第一行的时候,这些变数还是按照预设的空格作为分隔符的。那么怎么再读入第一行时就修改这个预设的分隔符呢?可以使用BEGIN。如下所示:
[root@localhost ~]# cat /etc/passwd | awk 'BEGIN {FS=":"} $3<10 {print $1 "\t" $3}'
root 0
bin 1
daemon 2
adm 3
lp 4
sync 5
shutdown 6
halt 7
mail 8
例3:薪资计算功能 创建一张pay.txt文件,文件中输入内容如下: Name 1st 2nd 3th VBird 23000 24000 25000 DMTsai 21000 20000 23000 Bird2 43000 42000 41000 计算每个人的工资总额,并按格式化输出
[root@localhost ~]# cat pay.txt | 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}'
Name 1st 2nd 3th total
VBird 23000 24000 25000 72000.00
DMTsai 21000 20000 23000 64000.00
Bird2 43000 42000 41000 126000.00
上面这个指令有几点要说明: 所有的动作,即在{}内的指令,如果存在多个指令,则每个指令建要用”;”来分隔或者按回车来分隔,否则会报错。与bash shell变量不一样,在awk中定义的变量可以直接使用。此外,awk的指令中支持使用if表达式,上面的指令也可以改成下面这样:
[root@localhost ~]# cat pay.txt | awk '{if(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}'
Name 1st 2nd 3th total
VBird 23000 24000 25000 72000.00
DMTsai 21000 20000 23000 64000.00
Bird2 43000 42000 41000 126000.00
例4:高级特性,for循环 新建一个reg.dat文件,在文件中输入内容: Mary O.S. Arch. Discrete Steve D.S. Algorithm Arch. Wang Discrete Graphics O.S. Lisa Graphics A.I. Lily Discrete Algorithm 第一行,表示学生名字,二三四行分别表示该学生所选择的课程,下面使用awk结合for循环以及阵列来统计每门课程的选课人数。
[root@localhost ~]# cat reg.dat | awk '{for(i=2;i<=NF;i++) Number[$i]++} END {for(course in Number) printf("%10s %1d\n",course,Number[course])}'
O.S. 1
Class1 1
Class2 1
Algorithm 1
D.S. 1
Graphics 2
Discrete 2
Arch. 1
Awk指令中第一个动作是{for(i=2;i<=NF;i++) Number[$i]++},该动作会将课程名称作为index,统计出每节课程所选人数作为相应的值。要注意,在linux中使用一个数组类型不需要声明,所以在上面这个动作中直接就开始使用Number这个数组了,而$i会将一列中的值全部取出来,作为number的index,然后再取出number[index]值做++操作,所以最后得到的number是以课程名称为index,课程的选课人数为value的数组。END表示必须等到所有的值全部遍历完才执行下面的动作。而第二个动作{for(course in Number) printf(“%10s %1d\n”,course,Number[course])}则是遍历该数组number,并将结果输出来。这些都是awk的高级特性,详细可以参考http://linux.vbird.org/linux_basic/0330regularex/awk.pdf