zoukankan      html  css  js  c++  java
  • awk命令详解

    shell三剑客:grep、sed、awk


    cut
    -c:字符数来截取 character
    -f:字段来截取 field
    -d:指定分隔符 默认是tab


    awk命令详解

    awk其实可以看做一种编程语言。
    awk的来源:三个人名:
    Alfred Aho,Peter Weinberger,Brian Kernighan

    awk工作原理

    gawk (gnu awk)

    • Unix中awk的GNU版本,完成grep和sed的工作 。
    • 支持数学运算,流程该控制内置大量的变量和函数。

    awk命令工作原理

    • 与sed一样, 均是一行一行的读取、处理 。
    • sed作用于一整行的处理, 而awk将一行分成数个字段来处理 。

    图解awk命令的简要处理流程:

    这里写图片描述

    awk的数据字段变量:

    • $0表示整行文本
    • $1表示文本中第一个数据字段
    • $2表示文本中第二个数据字段
    • $n表示文本中第n个数据字段

    awk的用-F来指定分隔符

    • 默认的字段分隔符是任意空白字符(空格或者TAB)

    awk命令的完整语法:

    awk ‘BEGIN{commands}pattern{commands}END{commands}’ file1

    • BEGIN是处理数据前执行的命令
    • END是处理数据后执行的命令

    注意:这里的commands指的是awk里面的子命令,并不是shell中的mkdir、ls等命令!!!

    例子:

    # cat passwd|awk -F: 'BEGIN{print "############"}$3<100{print $1,$3,$6}END{print "@@@@@@@@@@@@@@"}'

    执行结果:

    ############
    root 0 /root
    dbus 81 /
    rpc 32 /var/cache/rpcbind
    rpcuser 29 /var/lib/nfs
    haldaemon 68 /
    postfix 89 /var/spool/postfix
    mysql 27 /var/lib/mysql
    @@@@@@@@@@@@@@

    awk的命令的执行过程:

    1. 执行BEGIN{commands}语句块中的语句
    2. 从文件或stdin中读取第1行,
    3. 有无模式匹配, 若无则执行{}中的语句,
    4. 若有则检查该整行与pattern是否匹配, 若匹配, 则执行{}中的语句,
    5. 若不匹配则不执行{}中的语句,接着读取下一行
    6. 重复这个过程, 直到所有行被读取完毕
    7. 执行END{commands}语句块中的语句

    awk命令的基本语法:

    语法:awk -F 分隔符‘/模式/{动作}’ 输入文件

    • awk的指令一定要用单引号括起
    • awk的动作一定要用花括号括起
    • 模式可以是正则表达式、条件表达式或两种组合
    • 如果模式是正则表达式要用/定界符
    • 多个动作之间用;号分开

    分隔符在awk里有2种:

    • 输入分隔符 -F指定
    • 输出分隔符 OFS=

    例1:只有模式时,相当于grep

    # cat passwd|awk '/liu/'
    liu1:x:508:508::/home/liu1:/bin/bash
    liu2:x:509:509::/home/liu2:/bin/bash

    例2:只有动作时,就直接执行动作。

    # who|awk '{print $2}'
    tty1
    pts/0
    # who
    root     tty1         2016-06-24 23:20
    root     pts/0        2016-06-24 23:21 (liupeng.lan)

    例3:输出以h开头的行的第一列和第七列。

    # cat passwd|awk -F: '/^h/{print $1,$7}'
    haldaemon /sbin/nologin

    例4:显示不是以h开头的行的第一列和第七列

    awk -F: '/^[^h]/{print $1,$7}' /etc/passwd

    -F指定多个分隔符

    例5:以:或者/作为分隔符显示第1列和第10列

    awk -F'[:/]' '{print $1,$10}' /etc/passwd

    例6:以【空格:/】作为分隔符–》非常方便!!!
    这里写图片描述

    awk命令的操作符

    正则表达式和bash一致

    • 数学运算:+,-,*,/, %,++,- -
    • 逻辑关系符:&&, ||, !
    • 比较操作符:>,<,>=,!=,<=,== ~ !~
    • 文本数据表达式:== (精确匹配)
    • ~波浪号表示匹配后面的模式

    例1:在$2里查找匹配/pts/的,有就输出$1。

    # who | awk '$2 ~ /pts/{print $1}‘   

    例2:输出uid为两位的用户名和uid。

    # cat passwd|awk -F: '$3 ~/<..>/{print $1,$3}'   
    dbus 81
    rpc 32
    rpcuser 29
    haldaemon 68
    postfix 89
    mysql 27
    • < 以什么开头。
    • >以什么结尾。
    • ..正则里代表两个字符。

    例3:输出1~100能被5整除的或者以1开头的数字。

    #seq 100 | awk '$1 % 5 == 0 || $1 ~ /^1/{print $1}'

    例4:显示uid大于等于500并且家目录在/home下同时shell为bash结尾的用户名,uid,家目录,shell。

    # cat passwd|awk -F: '$3>=500&&$6 ~/^/home/&&$7 ~/bash/{print $1,$3,$6,$7}'
    aaa 500 /home/aaa /bin/bash
    stua6 501 /home/stua6 /bin/bash
    stuv1 502 /home/stuv1 /bin/bash
    stuv2 503 /home/stuv2 /bin/bash

    练习1:查找出用户名里包含liu的用户,输出用户名和uid及shell。
    答:

    # cat passwd|awk -F':' '$1  ~/liu/{print $1,$3,$6}'
    liu1 508 /home/liu1
    liu2 509 /home/liu2
    liu3 510 /home/liu3
    liu4 511 /home/liu4
    liu5 512 /home/liu5
    liu 539 /home/liu

    练习2:查找出/etc/passwd文件里用户名包含liu并且使用bash的用户。

    # cat passwd|awk -F':'  '$1 ~/liu/&&$7 ~/bash/{print $1,$7}'
    liu1 /bin/bash
    liu2 /bin/bash
    liu3 /bin/bash
    liu4 /bin/bash
    liu5 /bin/bash
    liu /bin/bash

    例题:

    # awk 'BEGIN{print "line one
    line two
    line three"}'
    line one
    line two
    line three
    # awk 'END{print "line one
    line two
    line three"}'line one
    按Ctrl+D才显示最后三行。
    line one
    line two
    line three

    ①awk里的变量的传递问题?

    • 可以再BEGIN部分定义,整个处理的过程都可以使用。END部分也可以使用。

    ②shell里的变量传递到awk里的问题?

    # cat -n passwd
    

    ……
    这里写图片描述

    例:显示文件的行数:

    # cat -n passwd|awk 'BEGIN{i=0}{i++}END{print i}'

    这里写图片描述

    分析:每取一行,i++。最后i的值就正好是passwd文件的行数。

    awk命令的内置变量

    名称 用途
    NF 每行$0的字段数
    NR 当前处理的行号
    FS 当前的分隔符,默认是空白字符
    OFS 当前的输出分隔符,默认是空白字符
    • NF:number of field,一行里有多少字段。
    • NR:number of record,当前处理的行号。
    • FS:field separater,输入分隔符。默认为空白(空格和Tab)。
    • OFS:out field separater,输出分隔符。

    例1:显示每行的字段数目

    #awk '{print NF}' /etc/passwd
    (默认分隔符是空白,所以字段数大都是1咯)

    结果如下图:
    这里写图片描述

    # awk -F: '{print NF}' /etc/passwd
    (指定了分隔符了,就是7咯)

    结果如下:
    这里写图片描述
    例2:显示每行的第一字段和最后一个字段

    #awk '{print $1,$NF}' /etc/passwd

    例3:显示每行的行号和内容

    #awk -F: '{print NR,$0}' /etc/passwd

    例4:显示第一列和第七列,中间用—隔开

    #awk -F: 'BEGIN{OFS="---"}{print $1,$7}' /etc/passwd

    简单写法:

    #awk -F: 'OFS="---"{print $1,$7}'/etc/passwd

    这里写图片描述

    例5:显示符合模式的用户名和所在的行号最后显示总行号:

    #awk 'BEGIN{FS=":"}/bash$/{print NR,$1}END{print NR}' /etc/passwd

    例6:显示文件的3到5行(带行号,带内容)

    #awkNR==3,NR==5{print NR,$0}’ /etc/passwd

    这里写图片描述
    显示4或7行(带行号,带内容):

    #awk 'NR==4||NR==7{print NR,$0}'   /etc/passwd

    这里写图片描述

    例7:显示文件的前10行(带行号,带内容):

    #awk   'NR<=10{print NR,$0}' /etc/passwd

    这里写图片描述

    例8:显示文件的前10行和30到40行:

    # awk 'NR<=10||NR>30&&NR<=40{print NR,$0}' /etc/passwd

    这里写图片描述

    练习:
    1.分析下面三条命令的区别,为什么
    ①#awk ‘BEGIN{print NR}’ /etc/passwd (执行一次)
    这里写图片描述

    ②#awk ‘{print NR}’ /etc/passwd (执行N次,N为行数)
    这里写图片描述
    (一直到48(最后一行))

    ③#awk ‘END{print NR}’ /etc/passwd (执行一次)
    这里写图片描述

    2.分析下面命令的执行结果
    ①#awk -F: ‘{print $NR}’/etc/passwd
    (解析:第1行去第1个字段,第2行取第2个字段,第n行去第n个字段……)
    这里写图片描述

    ②#awk -F: ‘{print NR, NF, 1,NF, $(NF-1)}’ /etc/passwd
    (输出每行的行号,字段数,用户名,最后一个字段,倒数第二个字段)
    这里写图片描述

    3.只显示df -h结果的第一列文件系统

    # df -h|awk -F' '  '{print $1}'  
    (注意指定分隔符为空白,默认就是空白)

    4.显示passwd文件的第5行和第10行的行号和用户名

    # cat passwd|awk -F: 'NR==5||NR==10{print NR,$1}'

    练习:

    1.使用NF变量显示passwd文件倒数第二列的内容

    # cat passwd|awk -F: '{print $(NF-1)}'

    2.显示passwd文件中第5到第10行的用户名

    # cat passwd|awk -F: 'NR>=5&&NR<=10{print $1}'

    3.显示passwd文件中第7列不是bash的用户名

    # cat passwd|awk -F: '$7 ~/[^bash]$/{print $1}' 
    --》[^bash]中括号里的^表示取反。

    或者:

    # cat passwd|awk -F: '$7 !~/bash/{print $1}'

    4.显示passwd文件中行号是5结尾的行号和行

    # cat passwd|awk -F: 'NR%10==5{print NR,$0}'
    # cat passwd|awk -F: 'NR ~/5$/{print NR,$0}' 

    5.用ifconfig只显示ip(不能使用tr或者cut命令)

    # ifconfig|awk -F: '/inet addr/{print $2}'|awk '{print $1}'
    192.168.1.147
    127.0.0.1
    # ifconfig |sed -n '/inet addr/p'|awk -F[:" "] '{print $13}'

    6.使用awk显示eth0的入站流量和出站流量(字节)

    # ifconfig eth0|awk -F'[: ]' '/RX bytes/{print $13,$19}'
    # ifconfig eth0|tr -s ' '|awk -F'[: ]' '/RX bytes/{print $4,$9 }'
    1025085 370703

    7.使用awk命令统计以r开头的用户数目,显示如下效果:
    这里写图片描述

    # cat passwd|awk -F: 'BEGIN{print "查找结果";i=0}/^r/{print $1;i++}END{print i}'
    查找结果
    root
    rpc
    rpcuser
    3

    awk命令的引用shell变量

    一、awk命令的引用shell变量

    • -v 引入shell变量

    例1:

    # name=haha
    # echo|awk -v abc=$name '{print abc,$name}'
    haha 
    # echo|awk -v abc=$name '{print $name}'
    
    # echo|awk -v abc=$name '{print abc}'
    haha    --》引用awk变量无需加$

    例2:

    # name=haha;soft=xixi
    # echo|awk -v abc=$name -v efg=$soft '{print abc,123}'
    haha 123

    分析:

    #!/bin/bash
    awk -v var=$1 -F:    '$1==var{print NR,$0}'  /etc/passwd
    [第1$1是位置变量,是执行此脚本时后面接的参数;第2$1指的是用户名。]

    这里写图片描述

    awk命令的函数

    awk编程语言内置了很多函数。

    length函数

    例1:利用length计算字符数目的函数来检查有无空口令用户。

    #awk -F: 'length($2)==0{print $1}' /etc/passwd /etc/shadow

    例2:显示文件中超过50个字符的行。

    # awk 'length($0)>50{print NR,$0}' /etc/passwd
    3 rpc:x:32:32:Rpcbind Daemon:/var/cache/rpcbind:/sbin/nologin
    4 rpcuser:x:29:29:RPC Service User:/var/lib/nfs:/sbin/nologin
    7 mysql:x:27:27:MySQL Server:/var/lib/mysql:/bin/bash

    system函数

    # cat list
    xixi 123
    haha 456
    hehe 789
    
    # awk '{system("useradd   "$1)}' list 
    # ls /home
    haha  hehe  hello  xixi
    # awk '{system("userdel -r  "$1)}' list  
    【命令用双括号括起,后面有空格】
    # ls /home
    hello

    练习:利用system函数给上述用户配置密码。

    #awk '{system("useradd $1");print $1 "is add"}'  list
    #awk '{system("userdel -r  "$1);print $1 "is deleted"}' list
    #awk '{system("echo  "$2"|passwd --stdin  "$1)}'  list
    【命令用双括号括起,后面有空格】

    awk命令的结构化语句

    if语句

    1.单分支

    #awk -F: '{if($1 ~ /<...>/)print $0}' /etc/passwd
    #awk -F: '{if($3 >= 500)print $1,$7}' /etc/passwd

    2.双分支

    #awk -F: '{if($3 != 0) print $1 ; else print $3}' /etc/passwd

    3.多分支

    #awk -F: '{if($1=="root") print $1;else if($1=="ftp") print $2;else if($1=="mail") print $3;else print NR}' /etc/passwd

    例2:利用awk的if多重分支判断用户的类型,root显示为管理员 。

    #awk -F: '{if($3==0) print $1,"管理员";else if($3>0 && $3<500) print $1,"系统用户";else if($3>=500 && $3 <= 60000) print $1,"普通用户";else print $1,"其它用户"}' /etc/passwd

    例:利用awk的system命令在/tmp下建立/etc/passwd中与用户名同名的目录 。

    #awk -F: '{system("mkdir /tmp/ "$1)}' /etc/passwd

    练习1:用1条命令实现将指定目录下大于10K的对象复制到/tmp下(禁止使用find 和for) 。

    #cp $(du -a  /boot | awk '$1>10240{print $2}') /tmp

    练习2:监控多台主机的磁盘分区一旦某台被监控主机的任一分区使用率大于80%, 就给root发邮件报警。

    #!/bin/bash
    warn=10
    ip=(10.10.10.2 10.10.10.3)
    for i in "${ip[@]}"
    do
       ssh root@$i df -Ph | tr -s " "|awk -v w=10 -F "[ %]" '/^/dev/{if($5>w) print  $1,$5"% useage is over 10%"}'>
    alert
       if [ -s alert ]
       then
           sed -i "1i $i" alert &&  mail -s "$i hd usage" root < alert
       fi
    done

    练习3:检查/var/log/secure日志文件,如果有主机用root用户连接服务器的ssh服务失败次数超过10次(10次必须使用变量),就将这个IP地址加入/etc/hosts.deny文件拒绝其访问,如果这个IP已经存在就无需重复添加到/etc/hosts.deny文件(要求使用awk语句进行字符过滤、大小判断和变量赋值,禁止使用echo、sed、grep、cut、tr命令)

    #/bin/bash
    awk '/Failed password for root/{print $(NF-3)}' /var/log/secure|sort -nr| uniq -c > test        --》sort 按降序排序,uniq -c统计次数
    NUM=10
    IP=$(awk '$1>num {print $2}' num=$NUM test)    --》$1就是统计好的次数
    for i in $IP
     do
       DENY=$(awk '$2==var {print $2}' var=$i /etc/hosts.deny)
       if [[ -z $DENY ]]
         then
          awk '$2==var {print "sshd: "$2}' var=$i test >> /etc/hosts.deny
          awk '$2==var {print "错误次数"$1,"拒绝"$2"访问"}' var=$i test
       else
          awk '$2==var {print "已经拒绝"$2"访问"}' var=$i test
       fi
    done

    awk对行和列的累加

    1.awk进行列求和
    ①统计/etc目录下以.conf结尾的文件的总大小

    #find /etc/ -type f -name "*.conf" |xargs ls -l | awk '{sum+=$5} END{print sum}‘

    这里写图片描述

    ②如果要匹配第一列的才累加,需要用到awk的数组和for循环(难点)

    cat xferlog | awk '{print $7,$8}' | sort -n >/lianxi/123.txt
    awk '{a[$1]+=$2}END{for(i in a) print i,a[i]}'/lianxi/123.txt | sort -rn -k2   
    
    解析:
    a[$1]=a[$1]+$2--》a[172.16.1.3]=老的a[172.16.1.3]+对应次数
    【xferlog是ftp服务器上的一个日志文件!】
    【awk里的数组支持shell里讲的关联数组!!】
    【sort -rn -k2是统计出下载量最大的ip并排序】

    ③awk进行行求和
    例1:#echo 1 2 3 4 5 | awk ‘{for(i=1;i<=NF;i++) sum+=$i; print sum}’

    执行结果:
    这里写图片描述

    例2:#seq -s ’ ’ 100 | awk ‘{for(i=1;i<=NF;i++) sum+=$i; print sum}’

    这里写图片描述

    PS:linux里记录行踪的地方

    ①history -c
    ②~/.bash_history
    ③/var/log/secure
    ④/var/log/lastlog
    ⑤/var/log/wtmp

    awk里面的数组是关联数组怎么理解:

    练习:awk里如何使用数组来存放数据?
    1.将所有的/etc/passwd所有的用户存放在user数组里。

    # cat /etc/passwd|awk -F: '{user[$1]}'

    解析:此时user[root]里面没有值,只是下标或者说索引变成了user[用户名]

    # cat /etc/passwd|awk -F: '{user[$1]=$3}'

    解析:此时,将$3的值(也就是uid)赋给user[$1]数组。
    这样,就把用户和用户对应的uid关联起来了,用户名做下标关键字,uid做数组元素对应的值。

    练习:awk里如何从数组里取出数据?
    2.将user数组里的所有值取出来。

    # cat /etc/passwd|awk -F: '{user[$1]=$3}END{for (i in user)print user[i],i}'

    这里写图片描述

    小结:

    awk里关联数组难点的理解:
    {a[$1]+=$2}

    $1对应的ip地址作为下标,将$2对应的大小赋值给下标为ip的元素。awk每读取一行就执行一次,重新又将$1对应的ip地址作为下标,执行a[$1]=a[$1]+$2,将上一行的a[$1]的值加上第2行$2值,实现累加的效果。但是ip地址还是那个ip地址,但是值已经累加了。

    适用于:某一列不变,而对应的另一列的内容不同的场景。

    练习:

    username    money
    feng                100
    feng                200
    feng                360
    li                     100
    li                     150
    zhang              90
    zhang              88

    统计每个人总共花了多少钱。并按总金额降序排序。

    #cat money.txt|awk '{username[$1]+=$2}END{for(i in username)print i,username[i]}'|sort -nr -k2

    这里写图片描述

    这里写图片描述

    awk里的关联数组之if判断

    练习2:/etc/passwd里的所有的用户的uid存放到一个数组,如果用户输入的用户名在数组里,就输出这个用户对应的uid。
    用户名做下标;uid做元素值。
    答案:

    #!/bin/bash
    read -p "Please input the username:" u_name
    cat /etc/passwd|awk -v U_name=$u_name -F: '{user[$1]=$3}END{if (U_name in user)print user[U_name]}'

    这里写图片描述

    补充:

    三种获得uid大于500小于10000的方法:

    # cat /etc/passwd|awk -F: '$3>500&&$3<10000{print $1,$3}'
    # cat /etc/passwd|awk -F: '($3>500&&$3<10000){print $1,$3}'
    # cat /etc/passwd|awk -F: '{if($3>500&&$3<10000)print $1,$3}'
  • 相关阅读:
    Object-C,NSSet,不可变集合
    NYIST 860 又见01背包
    NYIST 1070 诡异的电梯【Ⅰ】
    HDU 1542 Atlantis
    HDU 4756 Install Air Conditioning
    CodeForces 362E Petya and Pipes
    HDU 4751 Divide Groups
    HDU 3081 Marriage Match II
    UVA 11404 Palindromic Subsequence
    UVALIVE 4256 Salesmen
  • 原文地址:https://www.cnblogs.com/lpeng94/p/12546497.html
Copyright © 2011-2022 走看看