awk


调用有二种方式:

 

1,awk   -F"分隔符"  '{command}' filename

           分隔符为单个字符,可以省略“”,但是为多个字符时不能省略

2,是将所有的awk命令插入一个单独文件,然后调用

  awk -f awk-script-file  filename


awk内置变量

$0当前记录行,代表一行记录
$1~$n当前记录的第n个字段,字段间由FS分隔
FS输入字段分隔符,默认是空格
NF当前记录中的字段个数,就是有多少列,一般取最后一列字段
NR已经读出的记录数,就是行号,从1开始
RS输入的记录分隔符默认为换行符
OFS输出字段分隔符默是空格


字段的引用

$ 字段操作符

$1代表第一列,$2代表第二列。。。n以此类推

$0代表整个输入记录


比较:

sort命令排列文本行,并把文件打印输出到屏幕上。

sort -n -r

    -n 按算术值对数字字段排序。数字字段可包含前导空格、可选减号、十进制数字、千分位分隔符和可选基数符。

  -r 颠倒指定排序的顺序。

cut -d" "  -f1

wKiom1U15fiRX54KAAA64cbEk6A196.jpg


awk -F" " '{print $1}'

wKiom1U15luyNCRnAAAvfcQ×××A501.jpg



先用awk和cut做截取,来进行几个比较

比较一:用cut和awk截取IP,awk默认以N个空格为分隔符


[root@zhou1 ~]# ifconfig eth0 |grep Bcast |cut -d ":" -f2|cut -d" " -f1
10.0.10.192

[root@zhou1 ~]# ifconfig eth0 |grep Bcast |awk -F: '{print $2}'|awk  '{print $1}'
10.0.10.192

[root@zhou1 ~]# ifconfig eth0 |grep Bcast | awk -F" " '{print $2}'|awk -F":" '{print $2}'
10.0.10.192


比较二:awk能以多个字符作为分隔符,cut不能

下面都是以ab为分隔符,截取第二列的做法,cut报错,awk成功

[root@zhou1 ~]# echo "123ab456ab789" |cut -d"ab" -f2
cut: 分界符必须是单个字符
请尝试执行"cut --help"来获取更多信息。


[root@zhou1 ~]# echo "123ab456ab789" |awk -F"ab" '{print $2}'
456


打印所有行    awk '{print $0}' /etc/passwd

打印第一列 awk -F: '{print $1}' /etc/passwd

打印第一,三列  awk -F: '{print $1"\thaha\t"$3}' /etc/passwd



比较三:

[root@li test]# awk -F":" '{print $1" 的uid是 "$3}' /etc/passwd


--上面这条如果要用cut来实现,得写下面的脚本

#!/bin/bash

#

cat /etc/passwd | cut -d: -f1,3 |while read a

do

        head=`echo $a|cut -d: -f1`

        tail=`echo $a|cut -d: -f2`

        echo  "$head的uid是$tail"

done


比较四:

awk -F"[:)]"   --以:或)为分隔符

awk -F"[:)]*"--以多个连续的:或多个连续的)为分隔符



# who |tail -1

root     pts/0        2013-08-25 10:11 (:0.0)

# who |tail -1 |awk -F":|)" '{print $3}' --以:或)为分隔符

0.0


[root@zhou1 ~]# echo "1111/:::222///:3333/4444" |awk -F"[/:]*" '{print $3}' /test/2
3333


--象下面这样的字符串,以点为分隔符可以做,但是点号太多,不好数.所以可以用正则表达式,以连续的多个点来为分隔符


[root@zhou1 ~]# echo "haha,hehe.......................................heihei" |awk -F"[.]*" '{print $2}'heihei


=====================================================================================


awk  -F:  ‘BEGIN {处理文件前执行的代码块} {处理文件过程中执行的代码块} END {处理文件后执行的代码块}'   filename


BEGIN {

}

   {

}

END {

}

 

把上面的结构可以与下面的一个基本循环结构做对比

 

sum=0

for i in `seq 100`

do

sum=$[$sum+$i]

done

echo sum


# cat /etc/fstab |wc -l

10

# awk -F: 'BEGIN {print "这是第一行"} {print "这是第二行"} {print "这是第三行"} {print "这是第四行"} END {print "这是最后一行"}'  /etc/fstab |wc -l

32

--这个打印了32行,第一行一次,最后一行一次,中间的三行打了十次;3*10+2=32,正好对应fstab的行数;也就是说中间的代码循环的次数等同于后面的文件的行数


比较下面这两句

1、每一行循环都打印{print "用户名\t\tUID"}{print $1"\t"$3}

[root@zhou1 ~]# awk -F: '{print "用户名\t\tUID"}{print $1"\t"$3}' /etc/passwd
用户名        UID
root    0
用户名        UID
bin    1
用户名        UID


2、处理文件前执行的代码块{print "用户名\t\tUID"};处理文件过程中执行的代码块{print $1"\t"$3}

[root@zhou1 ~]# awk -F: 'BEGIN{print "用户名\t\tUID"}{print $1"\t"$3}' /etc/passwd
用户名        UID
root    0
bin    1
daemon    2



--下面两种结果相同,都是把列转成行

# head -1 /etc/passwd |awk -F":" '{print $1"\n"$2"\n"$3"\n"$4"\n"$5"\n"$6"\n"$7}'

# head -1 /etc/passwd |awk -F":" '{print $1} {print $2} {print $3} {print $4} {print $5} {print $6}{print $7}'


把/etc/passwd的第一行,按照:分为7列,左右倒序排列

 

# head -1 /etc/passwd |cut -d":" -f7,6,5,4,3,2,1

root:x:0:0:root:/root:/bin/bash

--用cut,列数反着写,还是没有倒序,所以应该写一个循环,然后使用echo -n去再排列出来 

 

换成awk来做

[root@zhou1 ~]# head -1 /etc/passwd
root:x:0:0:root:/root:/bin/bash


[root@zhou1 ~]# head -1 /etc/passwd |awk -F: '{print $7":"$6":"$5":"$4":"$3":"$2":"$1}'
/bin/bash:/root:root:0:0:x:root


[root@zhou1 ~]# head -1 /etc/passwd |awk -F: 'BEGIN {OFS=":"}{print $7,$6,$5,$4,$3,$2,$1}'
/bin/bash:/root:root:0:0:x:root


--OFS是一个内置的变量,表示定义输出的分隔符


--上面这可能会有一个问题,就是上面做的只有7列,如果我有700列,我不可能手动从$700写到$1吧;所以可以用awk的for循环来做.(后面有一个例子会讲到)



awk可以用做小数(浮点数)的运算

[root@zhou1 ~]# echo | awk '{print 2.13*2}'
4.26


=======================================================================================

--awk  内置变量

 

FS   设置分隔符,等同于-F

NF   代表字段数因为NF是列数,所以$NF就是代表最后一个列

NR   代表当前处理第几行

 

 

awk关系操作符

==   等于

!=    不等于

>    大于

<    小于

>=   大于等于

<=   小于等于

 

awk逻辑操作符

&&逻辑与

| |逻辑或

!非

 

 

awk运算操作符

+ - * / %

^  幂   比如:2的三次方  2^3   --shell里面求幂为2**3


练习:用netstat -ntl 截取所有开放监听的端口号

[root@zhou1 ~]# netstat -ntl  |grep -Ev "Active|Proto" |awk -F" " '{print $4}' |awk -F"[:]*" '{print $2}'
111
22
631
25
56579
111
22
1
1
56511


[root@zhou1 ~]# netstat -ntl  |grep -Ev "Active|Proto" |awk '{print $4}' |awk -F: '{print $NF}'
111
22
631
25
56579
111
22
631
25
56511


截取/etc/passwd前五行的倒数第二列

[root@zhou1 ~]# head -5 /etc/passwd |awk -F":" '{print $(NF-1)}'
/root
/bin
/sbin
/var/adm
/var/spool/lpd



练习:

cat 1.txt

1    2    3

1    2    3

 

cat 2.txt

a    b    c

a    b    c

 

要求得到结果

2    a    c

2    a    c

[root@zhou1 ~]# paste /test/1.txt /test/2.txt |awk -F " " '{print $2"\t"$4"\t"$6}'
2    a    c
2    a    c


[root@zhou1 ~]# paste /test/1.txt /test/2.txt |awk -F " " 'BEGIN{OFS="\t"}{print $2,$4,$6}'
2    a    c
2    a    c


打印第五行


[root@zhou1 ~]# head -5 /etc/passwd |tail -1
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin


[root@zhou1 ~]# awk 'NR==5 {print $0}' /etc/passwd
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin



[root@zhou1 ~]# awk '{if(NR==5) print $0}' /etc/passwd
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin


打印第五行的第五列

[root@zhou1 ~]# head -5 /etc/passwd |tail -1 |cut -d":" -f5
lp


[root@zhou1 ~]# awk -F":" 'NR==5 {print $5}' /etc/passwd
lp


[root@zhou1 ~]# awk -F":" '{if(NR==5) print $5}' /etc/passwd
lp


打印每一行的最后一列

[root@zhou1 ~]# awk -F":" '{print $NF}' /etc/passwd
/bin/bash
/sbin/nologin



找出/etc/以.conf结尾的文件的名字(如:kernelcap-2.6.18-164.el5.conf,只需要得到kernelcap-2.6.18-164.el5就可以了)

[root@zhou1 ~]# find /etc/ -name "*.conf" |awk -F/ '{print $NF}' |awk -F".conf" '{print $1}'


 

打印每行的字段数

[root@zhou1 ~]# awk -F: '{print NF}' /etc/passwd


打印第五行的字段数

[root@zhou1 ~]# awk -F: 'NR==5{print NF}' /etc/passwd
7


打印最后一行的最后一列

# awk -F: 'END {print $NF}' /etc/passwd


打印前五行

# awk 'NR<6 {print $0}' /etc/passwd


打印五到十行,并在前面加上行号

# cat -n /etc/passwd | awk 'NR>=5 && NR<11 {print $0}'

# awk 'NR>=5 && NR<11 {print NR,$0}' /etc/passwd

 

打印奇数行 (删除偶数行)

# awk 'NR%2==1 {print NR,$0}' /etc/passwd

 

打印偶数行 (删除奇数行)

# awk 'NR%2==0 {print NR,$0}' /etc/passwd


对/etc/passwd里的用户做分类,分成管理员,系统用户,普通用户(只显示用户名,用awk)

awk -F: '$3==0 {print $1}' /etc/passwd

awk -F: '$3>0 && $3<500 || $3==65534 {print $1}' /etc/passwd

awk -F: '$3>=500 &&  $3!=65534 {print $1}' /etc/passwd


找出每天18:30以后下班的打卡记录

120 张三  2013-7-31 18:19:28

120 张三  2013-7-30 18:58:45

120 张三  2013-7-30 22:41:47

120 张三  2013-7-29 22:15:23

[root@zhou1 ~]# cat /test/2 |awk -F"[: ]" '$4>18 || $4==18 && $5>=30 {print $0}'

[root@zhou1 ~]# cat /test/2 |awk -F" " '$4>="18:30:00" {print $0}'


假设9点整上班,算出下面这几天这几个人分别迟到多少次

张三 2013-8-25 9:19:28

李四 2013-8-25 8:58:45

王五 2013-8-25 8:41:47

马六 2013-8-25 9:12:52

田七 2013-8-25 9:01:47

张三 2013-8-24 8:49:28

李四 2013-8-24 8:54:45

王五 2013-8-24 9:11:47

马六 2013-8-24 9:02:52

田七 2013-8-24 9:04:47

张三 2013-8-23 9:29:28

李四 2013-8-23 8:57:45

王五 2013-8-23 8:41:47

马六 2013-8-23 9:08:52

田七 2013-8-23 9:09:47

张三 2013-8-22 9:24:28

李四 2013-8-22 9:16:45

王五 2013-8-22 9:11:47

马六 2013-8-22 8:52:52

田七 2013-8-22 8:44:47


[root@zhou1 ~]# cat /test/2 |awk -F" " '$3>="9:00:00"{print $1}' |sort |uniq -c |sort -n |awk -F" " 'BEGIN{print "迟到人姓名\t迟到次数"}{print $2"\t\t"$1}'

sort排序,uniq -c 统计




打印所有的列数的总和

--提示:awk是由上往下一行一行的扫描,类似写shell脚本时的循环语句,在这里是自动循环

--思路:先定义一个变量值为0,每扫一行,就加上那一行的列数(NF),最后打印出结果

 

[root@zhou1 ~]# awk -F: 'BEGIN {sum=0} {sum=sum+NF} END {print sum}' /etc/passwd
308


打印列数大于5的总行数

# awk -F: 'BEGIN {sum=0} NF>5 {sum=sum+1} END {print sum} ' /etc/passwd

# awk -F: 'BEGIN {sum=0} { if (NF>5) sum=sum+1} END {print sum} ' /etc/passwd

# awk -F: 'BEGIN {sum=0} {NF>5 && sum=sum+1} END {print sum} ' /etc/passwd




打印列数大于5的总行数和总列数

# awk -F: 'BEGIN {line=0;field=0} NF>5 { line=line+1;field=field+NF} END {print "大于5的总行数为"line"\n大于5的总列数为"field}' /etc/passwd--正确的写法


# awk -F: 'BEGIN {line=0;field=0}{ NF>5 && line=line+1;field=field+NF} END {print "大于5的总行数为"line"\n大于5的总列数为"field}' /etc/passwd--错误的写法,因为这里NF>5的条件只影响到了line没有影响到field

 

# awk -F: 'BEGIN {line=0;field=0} { if (NF>5)  line=line+1; field=field+NF} END {print "大于5的总行数为"line"\n大于5的总列数为"field}' /etc/passwd--错误的写法,因为这里NF>5的条件只影响到了line没有影响到field

 

# awk -F: 'BEGIN {line=0;field=0}{ NF>5 && line=line+1;NF>5 &&field=field+NF} END {print "大于5的总行数为"line"\n大于5的总列数为"field}' /etc/passwd--正确写法

 

# awk -F: 'BEGIN {line=0;field=0} { if (NF>5)  line=line+1; if (NF>5) field=field+NF} END {print "大于5的总行数为"line"\n大于5的总列数为"field}' /etc/passwd--正确写法




打印列数大于5小于8,并且行数为奇数行的总行数和总列数

[root@zhou1 ~]# awk -F: 'BEGIN{hang=0;lie=0} NF>5 && NF<8 && NR%2==1{hang=hang+1;lie=lie+NF} END{print "行数:"hang"\n列数:"lie}' /etc/passwd


 

统计/etc/passwd一共出现了多少个bash字符(要求使用awk)

# grep -o bash /etc/passwd |wc -l

# awk -F"bash" 'BEGIN {sum=0} { sum=sum+NF-1 } END {print sum}' /etc/passwd


=====================================================================================

--awk外部脚本

 

脚本的结构

BEGIN {

}

      {

}

END {

}


例 :把 awk -F: '{print $1}' /etc/passwd 改成写外部脚本的形式

 
#!/bin/bash
#
BEGIN{
        FS=":"   --FS  输入字段分隔符,默认是空格}
        {
        print $1
}


[root@zhou1 ~]# awk -f /test/3 /etc/passwd



打印字段数大于5的总行数  (用脚本写)

awk -F: 'BEGIN {sum=0} {if (NF>5) {sum=sum+1}} END {print sum}' /etc/passwd


BEGIN {

     FS=":"     --FS  输入字段分隔符,默认是空格

     sum=0

}

{

        if (NF>5) --或者把这两行写成  NF>5 && sum=sum+1  

        sum=sum+1     

}

END {

        print sum

}

打印列数大于5小于8,并且行数为奇数行的总行数和总列数(用awk脚本写)

[root@zhou1 ~]# awk -F: 'BEGIN{hang=0;lie=0} NF>5 && NF<8 && NR%2==1{hang=hang+1;lie=lie+NF} END{print "行数:"hang"\n列数:"lie}' /etc/passwd

BEGIN{

FS=":"

line=0

field=0

}

{

if (NF>5  && NF<8 && NR%2==1 )

{line=line+1; field=field+NF}

}

END{

print line"\n"field

}


===================================================================================

利用printf格式化输出

 

[root@li shell04]# awk 'BEGIN {print "hello world"}'

hello world

[root@li shell04]# awk 'BEGIN {printf "hello world"}'

hello world[root@li shell04]#


--从上面看到print和printf直接使用的主要区别就是printf不自动换行,需要使用\n来换行


格式:

printf {format_expression[,argument]}

两个主要的格式说明符是s和d,s代表字符串,d表示十进制整数


[root@zhou1 ~]# ls -l |grep -v 统计 |awk '{ printf ("%d%s\n",$5,$NF)}'
0128
2216anaconda-ks.cfg
72233install.log
13985install.log.syslog
4096公共的
4096模板
4096视频
4096图片
4096文档
4096下载
4096音乐
4096桌面


例:倒序排序所有字段(/etc/passwd) --因为/etc/passwd只有7列,所以可以直接写出来,但如果列数太多,就要用到下面的循环写法了

 

cat /etc/passwd |awk -F: '{print $7":"$6":"$5":"$4":"$3":"$2":"$1}'

 

 

BEGIN {

        FS=":"

}

{

        for (i=NF;i>0;i--){

                if (i != 1) {

                        printf("%s%s",$i,FS)

                }

                else {

                        printf ("%s",$i) 

                }

                }

                printf ("\n")

}

END {

}



调用:

 awk -f awk07.awk /etc/passwd

可以看到结果倒序排列


--awk字符匹配

 

==     完全精确匹配

~      匹配

!~     不匹配


# awk -F: '$3==65534 {print $1}' /etc/passwd

--这是通过uid的值,找用户名,条件用到了数值的匹配

nfsnobody(域名用户)


# awk -F: '$1=="nfsnobody" {print $3}' /etc/passwd--这反过来,匹配字符串,来查找其它内容;这里就要用到现在讲的字符匹配

65534


完全匹配

# awk -F: '$1=="oo" {print $0}' /etc/passwd

 

部分匹配

# awk -F: '$1~"oo" {print $0}' /etc/passwd

# awk -F: '$1~/oo/ {print $0}' /etc/passwd


不匹配

# awk -F: '$1!="oo" {print $0}' /etc/passwd

# awk -F: '$1!~"oo" {print $0}' /etc/passwd


找a用户的uid

# cat /etc/passwd |grep ^a: |cut -d":" -f3

# awk -F: '$1=="a" {print $3}' /etc/passwd


查找用户名里有a字母的用户个数

 

# grep ^.*a.*: /etc/passwd |wc -l--错误写法(因为它无法定义:符号是第几个)

# grep .*a.*:.*:.*:.*:.*:.*:.* /etc/passwd  | wc -l  --正确

# grep ^[^:]*a[^:]*: /etc/passwd |wc -l   --正确

# awk -F: '$1~"a" {print $1}' /etc/passwd |wc -l

# awk -F: 'BEGIN {sum=0} $1~"a" {sum=sum+1} END {print sum}' /etc/passwd

例:统计/etc/passwd里以/sbin/nologin结束的用户名,把用户名打印出来,并统计个数

--不用awk的写法,两句命令

# cat /etc/passwd |grep /sbin/nologin$ |cut -d":" -f1

# cat /etc/passwd |grep /sbin/nologin$ |cut -d":" -f1 |wc -l

# awk -F: 'BEGIN {sum=0} $NF=="/sbin/nologin" {printf ("%s ",$1) ;sum=sum+1} END {print "\n一共有:"sum"个"}' /etc/passwd


========================================================================================


--awk的字符串函数

 

长度函数 length()


打印出/etc/passwd的所有用户名,并统计其字符长度

awk -F: '{print $1,length($1)}' /etc/passwd

[root@li shell04]# head -1 /etc/passwd |awk -F: '{print length($0)}'

31

[root@li shell04]# head -1 /etc/passwd | wc -L

31


例:查找出用户名长度大于5的用户,显示用户名

# awk -F: 'length($1)>5 {print $1}' /etc/passwd


例: 查找/etc/passwd文件里一共有多少个字符

awk 'BEGIN {sum=0} {sum=sum+length($0)} END {print sum}' /etc/passwd


wc -m /etc/passwd   --这种算法会把每行的换行符也算一个字符加上去,所以这条命令的结果会比上面awk算的结果要正好大于文件的行数


index  位置函数

 

找出oo字符串在/etc/passwd的所有用户名内的第几位

# awk -F: '{print index($1,"oo")}' /etc/passwd

大小转换

toupper

tolower

 

# awk -F: '{print tolower(toupper($1))}' /etc/passwd


substr  截取函数

# awk -F: '{print substr($1,1,2)}' /etc/passwd

--把$1从第一个字符开始,截取2个(而不是从第一个到第二个)


# echo 12345356346343234sfsahaha34523 |awk '{print substr($0,index($0,"haha"),4)}'

haha



例:截取下面这条命令结果里的4-7个字符

# head -1 /etc/passwd

root:x:0:0:root:/root:/bin/bash

 

方法一:

# head -1 /etc/passwd |cut -c4-7

方法二:  --大写F后和两个连着的引号中间要有一个空格才行

# head -1 /etc/passwd |awk -F "" '{print $4$5$6$7}'

方法三: 

# head -1 /etc/passwd |awk 'BEGIN{FS=""} {print $4$5$6$7}'

方法四:

# head -1 /etc/passwd | awk -F: '{print substr($0,4,4)}'

方法五:

a=`head -1 /etc/passwd`

echo ${a:3:4}

方法六:

head -1 /etc/passwd |sed -r 's/(...)(....)(.*)/\2/g'


例:查找当前目录下子目录(不包含子目录下的子目录)的目录名

方法一:

# find /etc/ -type d -maxdepth 1

方法二:

# ll /etc/ | awk 'substr($0,1,1)=="d" {print $NF}' 

方法三:

for i in /etc/*

do

[ -d $i ] && echo $i

done

方法四:

# ll /etc/ |grep ^d | awk '{print $NF}'


替换函数

sub  单替换

gsub  全替换


[root@li shell04]# awk -F: 'NR==1 {sub(/o/,"O",$0);print $0}' /etc/passwd 

rOot:x:0:0:root:/root:/bin/bash 


[root@li shell04]# awk -F: 'NR==1 {sub(/o/,"O");print $0}' /etc/passwd 

rOot:x:0:0:root:/root:/bin/bash

 

[root@li shell04]# awk -F: 'NR=={gsub(/o/,"O",$0);print $0}' /etc/passwd 

rOOt:x:0:0:rOOt:/rOOt:/bin/bash

 

[root@dns shell04]# awk -F: 'NR==1 {gsub(/\/bin\/bash/,"/sbin/nologin",$0);print $0}' /etc/passwd 

root:x:0:0:root:/root:/sbin/nologin


[root@zhou1 ~]# awk -F: 'NR==1 {gsub("/bin/bash","/sbin/nologin");print $0}' /etc/passwd
root:x:0:0:root:/root:/sbin/nologin