Linux Shell编程及自动化运维实现 第5章 三剑客 awk(三剑第三剑)

目录

语法

内部变量  

格式化输出

    printf 函数        

模式(正则表达)和动作

awk脚本编程

    变量awk调用变量

    条件&判断

    循环

    数组

练习示例:


前言
    awk 是一种编程语言,用于在linux/unix下对文本和数据进行处理。
数据可以来自标准输入、一个或多个文件,或其它命令的输出。
它支持用户自定义函数和动态正则表达式等先进功能,


awk的处理文本和数据的方式是这样的,
它逐行扫描文件,从第一行到最后一行,
寻找匹配的特定模式的行,并在这些行上进行你想要的操作。
如果没有指定处理动作,则把匹配的行显示到标准输出(屏幕),

awk分别代表其作者姓氏的第一个字母。因为它的作者是三个人,
分别是Alfred Aho、Peter Weinberger、 Kernighan。

工作原理
    # awk -F: '{print $1,$3}' /etc/passwd
(1)awk使用一行作为输入,并将这一行赋给内部变量$0,每一行也可称为一个记录,以换行符结束

(2)然后,行被:(默认为空格或制表符)分解成字段(或域),每个字段存储在已编号的变量中,从$1开始,
最多达100个字段

(3)awk输出之后,将从文件中获取另一行,并将其存储在$0中,覆盖原来的内容,然后将新的字符串分隔
成字段并进行处理。该过程将持续到所有行处理完毕
    
 

语法


    awk   [options]   'commands'      filenames (推荐)
    ==options:
        例如:-F 定义输入字段分隔符,默认的分隔符是空格或制表符(tab)
    ==command(时空):
        BEGIN{}   {}    END{}
            BEGIN{} 
                begin发生在行处理前(注意大写)
            {}
                行处理时,读一行执行一次
            END{}
                行处理后
        示例
            #awk 'BEGIN{print 1/2} {print "ok"} END{print "-----------"}' /etc/hosts
                0.5
ok
ok
ok
-----------
            # awk 'BEGIN{print 1/2}{print "-----";print $0}END{print "HOHO"}' /etc/hosts
                0.5
-----
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
-----
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
HOHO


内部变量
  

 FS 输入字段分隔符(默认空格)
            

[root@localhost ~]# awk -F: '{print $1, $3}' /etc/passwd | head -1
root 0

[root@localhost ~]# awk -F'[ :\t]' '{print $1,$2,$3}' /etc/passwd | head -1
root x 0

[root@localhost ~]# awk 'BEGIN{FS=":"} {print $1,$3}' /etc/passwd | head -1
root 0
    OFS
        输出字段分隔符

            [root@localhost ~]# awk -F: '{print $1,$2,$3,$4}' /etc/passwd | head -1
root x 0 0

[root@localhost ~]# awk -F: 'BEGIN{FS=":";OFS="+++"}{print $1,$2,$3,$4}' /etc/passwd | head -1
root+++x+++0+++0

    RS
        输入记录(行)分隔符,默认换行符

            [root@localhost ~]# awk   '{print $0}' a.txt 
111 222 333 444 555:666:777
[root@localhost ~]# awk 'BEGIN{RS="   "}{print $0}' a.txt 
111
222
333
444
555:666:777
请注意,在此时记录已经不是行的概念了。分隔符由”换行符“换成了”空格“

[root@localhost ~]# awk 'BEGIN{RS=" ";ORS="+++"}{print $0}' a.txt 
111+++222+++333+++444+++555:666:777
输出记录分隔符换成了“+++”。如果不指定输出记录分隔符会如何呢?
    ORS输出记录(行)分隔符,默认换行符


    FNR多文件独立编号
            同下


    NR多文件汇总编号
            [root@localhost ~]# awk -F: '{print NR, $0}' /etc/centos-release /etc/hosts
1 CentOS Linux release 7.3.1611 (Core) 
2 127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
3 ::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
[root@localhost ~]# awk -F: '{print FNR, $0}' /etc/centos-release /etc/hosts
1 CentOS Linux release 7.3.1611 (Core) 
1 127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
2 ::1         localhost localhost.localdomain localhost6 localhost6.localdomain6


    NF字段总数
            [root@localhost ~]# awk -F: '{print NF, $0}'  /etc/passwd
7 root:x:0:0:root:/root:/bin/bash
7 bin:x:1:1:bin:/bin:/sbin/nologin
7 daemon:x:2:2:daemon:/sbin:/sbin/nologin

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

 

格式化输出


    print  函数
        [root@localhost ~]# date |awk '{print "Month: " $2 "\nYear: " $1}'
Month: 11月
Year: 2017年

            \n换行符
            想输出文字,用引号
        [root@localhost ~]# awk -F: '{print "username is: " $1 "\t uid is: " $3}' /etc/passwd | head -1
username is: root     uid is: 0
        [root@localhost ~]# awk -F: '{print "\tusername and uid: " $1,$3 "!"}' /etc/passwd  | head -1
    username and uid: root 0!


    printf 函数
        

语法
            %s 字符类型
            %d 数值类型
            %f 浮点型,可以定义保留
            占15字符
            - 表示左对齐,默认是右对齐
            printf默认不会在行尾自动换行,加\n
            , 逗号,输出字段分隔符


        示例
            [root@localhost ~]# awk -F: '{printf "%-10s %-10s %-15s\n", $1,$2,$3}' /etc/passwd | head 
root       x          0              
bin        x          1              
daemon     x          2              
adm        x          3              
lp         x          4              
sync       x          5              
shutdown   x          6              
halt       x          7              
mail       x          8              
operator   x          11  
这是由于xmind的对齐导致的。
            [root@localhost ~]# awk -F: '{printf "|%-15s| %-10s| %-15s|\n", $1,$2,$3}' /etc/passwd | head
|root           | x         | 0              |
|bin            | x         | 1              |
|daemon         | x         | 2              |
|adm            | x         | 3              |
|lp             | x         | 4              |
|sync           | x         | 5              |
|shutdown       | x         | 6              |
|halt           | x         | 7              |
|mail           | x         | 8              |
|operator       | x         | 11             |

            [root@localhost ~]# awk -F: '{printf "|%-15s| %-15s| %0.1f|\n", $1,$2,$3}' /etc/passwd | head
|root           | x              | 0.0|
|bin            | x              | 1.0|
|daemon         | x              | 2.0|
|adm            | x              | 3.0|
|lp             | x              | 4.0|
|sync           | x              | 5.0|
|shutdown       | x              | 6.0|
|halt           | x              | 7.0|
|mail           | x              | 8.0|

 

模式(正则表达)和动作

概念
    
        任何awk语句都由模式和动作组成。模式部分决定动作语句何时触发及触发事件。
如果省略模式部分,动作将时刻保持执行状态。每一行都会有动作。
模式可以是任何条件语句或复合语句或正则表达式。有模式的话,就是对模式对应的行进行动作。
        模式:可以是条件测试,正则,复合语句
动作:可以是打印,计算等。
    字符串比较
        # awk    '/^root/'     /etc/passwd
# awk '$0 ~ /^root/' /etc/passwd
# awk '$0!~/^root/' /etc/passwd
# awk -F: '$1 ~ /^root/' /etc/passwd


    数值比较
        目的
            比较表达式采用对文本进行比较,只有当条件为真,才执行指定的动作。比较表达式使用关系运算符,
用于比较数字与字符串。
        关系运算符
            语法
                运算符 含义 示例
<     小于             x<y
<=     小于或等于     x<=y
==     等于             x==y
!=     不等于        x!=y
>=     大于等于         x>=y
>     大于             x>y
            示例
                # awk -F: '$3 == 0' /etc/passwd 
# awk -F: '$3 == 1' /etc/passwd 
# awk -F: '$3 < 10' /etc/passwd
== 也可以用于字符串判断
# awk -F: '$7 == "/bin/bash"' /etc/passwd
# awk -F: '$1 == "alice"' /etc/passwd
        算术    运算
            语法
                + - * / %(模) ^(幂2^3) 
            示例
                # awk -F: '$3 * 10 > 500' /etc/passwd


    多条件
        逻辑操作符和复合模式
            语法
                && 逻辑与 a&&b
|| 逻辑或 a||b
! 逻辑非 !a
            学员练习
                # awk -F: '$1~/root/ && $3<=15'     /etc/passwd
# awk -F:     '$1~/root/ || $3<=15'         /etc/passwd
# awk -F:     '!($1~/root/ || $3<=15)'     /etc/passwd
        范围模式
            语法
                awk '/从哪里/,/到哪里/' filename
            学员练习
                # awk -F: '/adm/,/lpd/' /etc/passwd
从adm到ldp,显示出来,注意避免匹配重复的字段。


awk脚本编程


    变量awk调用变量


            自定义内部变量 -v
                awk -v user=root -F: '$1 == user' /etc/passwd
-v定义变量
            外部变量 “ ‘ ”
                双引号
                    # var="bash"
# echo "unix script" | awk "{print "123",\"$var\"}"
123 bash
注意 awk调用外部变量时,外部使用双引号,内部也使用双引号,但需要转义内部的双引号
                单引号
                    # var="bash"
# echo "unix script" |awk '{print $1,"'"$var"'"}'
unix  bash
注意使用单引号时,内部需要用双引转义


    条件&判断


        if语句
            格式
                {if(表达式){语句;语句;...}}
            示例
                需求
                    如果$3是0,就说他是管理员
                awk -F: '{if($3==0) {print $1 " is administrator."}}' /etc/passwd
                需求
                    统计系统用户数
                awk -F: '{if($3>0 && $3<1000){count++;}} END{print count}' /etc/passwd 


        if...else语句
            格式
                {if(表达式){语句;语句;...}else{语句;语句;...}}
{if(){}else{}}
            示例
                需求
                    如果第三列是0,打印该行第一列,否则打印第七列,登录shell
                示例
                    awk -F: '{if($3==0){print $1} else {print $7}}' /etc/passwd
                需求
                    统计管理员和系统用户数量
                示例
                    awk -F: '{if($3==0){count++} else{i++}} END{print "管理员个数: "count ; print "系统用户数: "i}' /etc/passwd


        if...else if...else语句:
            格式
                {if(表达式1){语句;语句;...}else if(表达式2){语句;语句;...}else if(表达式3){语句;语句;...}else{语句;语句;...}}
                if (条件){动作}elseif(条件){动作}else{动作}
                if(){}else if  (){}else if(){}else{}
            示例
                需求
                    题目:显示出三种用户的信息
管理员:管理员ID为0

内置用户:用户ID<1000

普通用户: 用户ID>999
                示例
                    [root@localhost ~]# awk -F:  '{if($3==0){print $1," is admin "}else if ($3>999){print $1," is user"}else {print $1, " is sofo user"}}'  /etc/passwd
                        示范
                            root  is admin 
bin  is sofo user
daemon  is sofo user
adm  is sofo user
lp  is sofo user
sync  is sofo user
shutdown  is sofo user
halt  is sofo user
mail  is sofo user
operator  is sofo user
games  is sofo user
ftp  is sofo user
nobody  is sofo user
systemd-network  is sofo user
dbus  is sofo user
polkitd  is sofo user
tss  is sofo user
postfix  is sofo user
chrony  is sofo user
sshd  is sofo user
aofa  is user
apache  is sofo user

            示例
                题目:统计出三种用户的数量
管理员数量:管理员ID为0

内置用户数量:用户ID<1000

普通用户数量: 用户ID>999
                    awk -F: '{

if($3==0){i++}
else if($3>999){k++}
else{j++}

}

END{
print i;
print k;
print j

}' /etc/passwd
                示例
                    awk -F: '{if($3==0){i++} else if($3>999){k++} else{j++}} END{print i; print k; print j}' /etc/passwd
                升级
                    awk -F: '{if($3==0){i++} else if($3>999){k++} else{j++}} END{print "管理员个数: "i; print "普通用个数: "k; print "系统用户: "j}' /etc/passwd


    循环


        while
            循环打印10个数字
                awk 'BEGIN{  while(i<=10){print i; i++} }'
                    示范
                        [root@localhost ~]# awk 'BEGIN{ i=1; while(i<=10){print i; i++} }' 
1
2
3
4
5
6
7
8
9
10
            第一行打印十次
                awk -F: '{ while(i<=9) {print $0; i++}}'  passwd


        for
            循环打印5个数字
                awk 'BEGIN{for(i=1;i<=5;i++){print i} }'
                    不适用BEGIN可以吗?
{},和END{}可以吗?可以需要有文件。
            将每行打印10次
                awk -F: '{ for(i=1;i<=10;i++) {print $0} }' /etc/passwd
            打印每一行的每一列
                 awk -F: '{ for(i=1;i<=NF;i++) {print $i} }' passwd
                    说明:NF是最大列数,循环打印了每一列。
                    # awk -F: '{ for(i=1;i<=NF;i++) {print $i} }' passwd
root
x
0
0
root
/root
/bin/bash


    数组


        定义数组
          需求
                将用户名定义为数组的值,打印第一个值
            # awk -F: '{username[++i]=$1} END{print username[1]}' /etc/passwd
root


        数组遍历 按索引遍历
                [root@localhost ~]#  awk -F: '{username[x++]=$1} END{for(i in username) {print i,username[i]} }' /etc/passwd
10 games
11 ftp
12 nobody
13 systemd-bus-proxy
14 systemd-network
15 dbus
16 polkitd
30 chrony
17 abrt
31 ntp
18 unbound

    awk编程案例
        1. 统计/etc/passwd中各种类型shell的数量
             awk -F: '{shells[$NF]++} END{ for(i in shells){print i,shells[i]} }' /etc/passwd
            提示
                $NF 最后一列的字段内容
{}行处理
把统计的对象,作为索引。每次递增。
Print i  打印索引
Shells[i]  数组加索引,显示的就是值。
        2. 统计Apache/Nginx日志中的访问前十 <统计日志>
            cat  access_log |awk '{ips[$1]++} END{for(i in ips){print i,ips[i]} }' |sort -k2 -rn |head


练习示例:


    # awk '/west/' datafile
        匹配west
    # awk '/^north/' datafile
        以north开头
    # awk '$3 ~ /^north/' datafile
        第三列 匹配 north开头的
    # awk '/^(no|so)/' datafile
        no或者so 开头
    # awk '{print $3,$2}' datafile
        打印第2第3列,逗号引用分隔符。默认引用OFS=" "
    # awk '{print $3 $2}' datafile
        打印第2第3列,没有分隔符
    # awk '{print $0}' datafile
        打印记录,默认整行
    # awk 'END {print "Number of fields: "NF}' datafile 
        格式化文字“字段数”:列数
            [root@localhost ~]# awk '{print "Number of fields: "NF}'  /etc/hosts
Number of fields: 5
Number of fields: 5
[root@localhost ~]# awk '{print "Number of fields: "$ NF}'  /etc/hosts 第五列
Number of fields: localhost4.localdomain4
Number of fields: localhost6.localdomain6
    # awk '/northeast/{print $3,$2}' datafile
        匹配north行的第3,2列
    # awk '/E/' datafile
        匹配包含E的所有行
    # awk '/^[ns]/{print $1}' datafile
        以n或s开头行的第一列
    # awk '$5 ~ /\.[7-9]+/' datafile
        第五列 匹配 .1到多个7,8,9的行
    # awk '$2 !~ /E/{print $1,$2}' datafile
        第二列 中不包含E的行,打印第1,2列。
    # awk '$3 ~ /^Joel/{print $3 " is a nice boy."}' datafile
        在第三列中匹配 以Joel开头的行,打印行中的第3列
    # awk '$8 ~ /[0-9][0-9]$/{print $8}' datafile
        匹配第八列,两个数字结尾,打印第八列
    # awk '$4 ~ /Chin$/{print "The price is $" $8 "."}' datafile
        在第四列中 匹配 Chin结尾的字段, 格式化文字加第八列带个点
    # awk '/Tj/{print $0}' datafile
        匹配Tj的行
    # awk -F: '{print $1}' /etc/passwd
        冒号分割打印第一列
    # awk -F"[ :]" '{print NF}' /etc/passwd
        以冒号或空格作为字段分隔符 ,并打印列数
    # awk -F"[ :]+" '{print NF}' /etc/passwd 
        以1到多个冒号或空格作为字段分隔符 ,并打印列数
    # awk '$7 == 5' datafile
        打印第七列是5 的行
    # awk '$2 == "CT" {print $1, $2}' datafile
        打印第二列是CT的行的第1,2列
    # awk '$7 != 5' datafile
        打印第七列不等于5的行
    # awk '$7 < 5 {print $4, $7}' datafile
        打印第七列小于5的行
    # awk '$6 > .9 {print $1,$6}' datafile
        打印第6列大于.9的行的第1,6列
    # awk '$8 <= 17 {print $8}' datafile
        打印第八列小于等于17的行的第八列
    # awk '$8 >= 17 {print $8}' datafile
        打印第八列大于等于17的行的第八列
    # awk '$8 > 10 && $8 < 17' datafile
        第八列大于10,并小于17的行。
    # awk '$2 == "NW" || $1 ~ /south/ {print $1, $2}' datafile
        第二列有NW,或者,第一列包含south的行,只打印第1,2列
    # awk '!($8 == 13){print $8}' datafile
        第八列不等于13,打印匹配行的第八列($8!=13)
    # awk -F":" '/root/  {print $3 + 10}' /etc/passwd
        匹配root字段,第三列+10并打印。
    # awk '/southem/{print $8 + 10}' datafile
        匹配southerm,打印第八列+10
    # awk '/southem/{print $5 + 10.56}' datafile
        匹配southerm,打印第5列+10.56
    # awk '/southem/{print $8 - 10}' datafile
        匹配southerm,打印第八列-10
    # awk '/southem/{print $8 / 2 }' datafile
        匹配southerm,打印第八列除以2
    # awk '/southem/{print $8 / 3 }' datafile
        匹配southerm,打印第八列除以3
    # awk '/southem/{print $8 * 2 }' datafile
        匹配southerm,打印第八列乘以2
    # awk '/southem/{print $8 % 2 }' datafile
        取余数
    # awk '$3 ~ /^Suan/ {print "Percentage: "$6 + .2 " Volume: " $8}' datafile
        第三列正则匹配以Suan开头的,打印第6列加0.2 并且打印第八列
    # awk '/^western/,/^eastern/' datafile
        从以westem开头到以eastem开头的行
    # awk '{print ($7 > 4 ? "high "$7 : "low "$7)}' datafile 
        //三目运算符 a?b:c 条件?成立结果1:不成立结果2
第三列小于4显示high$7
第三列大于4显示low$7
    # awk '$3 == "Chris" {$3 = "Christian"; print $0}' datafile
         赋值运算符,$3=christian
[root@localhost ~]# awk -F":" '$3 == 0 {$3="chaoji";print $0}' /etc/passwd
root x chaoji 0 root /root /bin/bash
    # awk '/Derek/ {$8+=12; print $8}' datafile  
        匹配Derek的行,为$8列赋值,$8=$8+12,再打印出$8
    # awk '{$7%=3; print $7}' datafile 
        //$7 %= 3等价于$7 = $7 % 3
先将第七列取余,再赋值给第七列,打印第七列

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值