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}'
原文地址:https://www.cnblogs.com/lpeng94/p/12546497.html