-- awk 调用有二种方式: 1,awk -F"分隔符" "command" filename 2,是将所有的awk命令插入一个单独文件,然后调用 awk -f awk-script-file filename 字段的引用 $字段操作符 $1代表第一列,$2代表第二列。。。n以此类推 $0代表整个输入记录 比较: cut -d" " -f1 sort -t" " -k1 awk -F" " '{print $1}' 先用awk和cut做截取,来进行几个比较 比较一:用cut和awk截取IP ifconfig eth0 |grep Bcast |cut -d ":" -f2|cut -d" " -f1 ifconfig eth0 |grep Bcast |awk -F: '{print $2}'|awk '{print $1}' ifconfig eth0 |grep Bcast | awk -F" " '{print $2}'|awk -F":" '{print $2}' --awk默认以N个空格为分隔符 比较二: cut不能以多个字符为分隔符,而awk 可以 下面都是以ab为分隔符,截取第二列的做法,cut报错,awk成功 # echo 123ab456ab789 |cut -d"ab" -f2 cut: the delimiter must be a single character Try `cut --help' for more information. # 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 # 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":" '{print $1"的uid是"$3",登录shell是"$7}' /etc/passwd 比较四: # who |tail -1 root pts/0 2013-08-25 10:11 (:0.0) # who |tail -1 |awk -F":|)" '{print $3}' --以:或)为分隔符 0.0 # who |tail -1 root pts/3 2014-08-14 09:46 (10.1.1.111) who |tail -1 |awk -F[\(\)] '{print $2}' --以(或)为分隔符,加转义符 10.1.1.111 awk -F"[:)]" --以:或)为分隔符 awk -F"[:)]*" --以多个连续的:或多个连续的)为分隔符 [root@li ~]# echo "haha,hehe.heihei" |awk -F"[,.]" '{print $1}' haha [root@li ~]# echo "haha,hehe.heihei" |awk -F"[,.]" '{print $2}' hehe [root@li ~]# echo "haha,hehe.heihei" |awk -F"[,.]" '{print $3}' heihei --象下面这样的字符串,以点为分隔符可以做,但是点号太多,不好数.所以可以用正则表达式,以连续的多个点来为分隔符 # echo "haha,hehe.......................................heihei........." |awk -F"[.]*" '{print $2}' heihei ================================================================ $n n不一定要用整数,也可以用变量值代替 echo a b c |awk 'BEGIN {one=1;two=2} {print $(one+two)}' 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的行数;也就是说中间的代码循环的次数等同于后面的文件的行数 比较下面这两句 awk -F: '{print "用户名\t\tUID"}{print $1"\t"$3}' /etc/passwd awk -F: 'BEGIN{print "用户名\t\tUID"}{print $1"\t"$3}' /etc/passwd 用户名 UID root 0 用户名 UID bin 1 用户名 UID root 0 bin 1 --下面两种结果相同,都是把列转成行 # 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}' # head -1 /etc/passwd |awk -F: 'BEGIN {print "*************************"} {print "*\t"$1"\t\t*"} {print "*\t"$2"\t\t*"} {print "*\t"$3"\t\t*"} {print "*\t"$4"\t\t*"} {print "*\t"$5"\t\t*"} {print "*\t"$6"\t\t*"} {print "*\t"$7"\t*"} END {print "*************************"}' ************************* * root * * x * * 0 * * 0 * * root * * /root * * /bin/bash * ************************* # head -1 /etc/passwd |awk -F: 'BEGIN {print "*************************"} {print "*\t"$1"\t\t*\n*\t"$2"\t\t*\n*\t"$3"\t\t*\n*\t"$4"\t\t*\n*\t"$5"\t\t*\n*\t"$6"\t\t*\n*\t"$7"\t*"} END {print "*************************"}' ************************* * root * * x * * 0 * * 0 * * root * * /root * * /bin/bash * ************************* =============================================================== 例 把/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来做 # head -1 /etc/passwd |awk -F: '{print $7":"$6":"$5":"$4":"$3":"$2":"$1}' # head -1 /etc/passwd |awk -F: 'BEGIN {OFS=":"}{print $7,$6,$5,$4,$3,$2,$1}' --OFS是一个内置的变量,表示定义输出的分隔符 --上面这可能会有一个问题,就是上面做的只有7列,如果我有700列,我不可能手动从$700写到$1吧;所以可以用awk的for循环来做.(后面有一个例子会讲到) awk可以用做小数(浮点数)的运算 echo $[1.23*2] --错误做法 echo |awk '{print 1.23*2}' --正确做法 echo 1.23*2 | bc --正确做法 ===================================================================== --awk 内置变量 FS 代表输入的分隔符,等同于-F ; OFS代表输出的分隔符 NF 代表字段数 因为NF是列数,所以$NF就是代表最后一个列 NR 代表当前处理第几行 awk关系操作符 == 等于 != 不等于 > 大于 < 小于 >= 大于等于 <= 小于等于 awk逻辑操作符 && 逻辑与 类似shell里的[ 条件1 -a 条件2 ] || 逻辑或 类似shell里的[ 条件1 -o 条件2 ] ! 非 awk运算操作符 + - * / % ^或** 幂 比如:2的三次方 2^3 或2**3 --shell里面求幂只能为2**3 练习:用netstat -ntl 截取所有开放监听的TCP协议端口号 # netstat -ntl |awk 'NR >2 {print $4}'|awk -F: '{print $NF}' 练习:用netstat -ntl 截取所有开放监听的TCP协议端口号(只要IPv4的监听所有的开放端口) # netstat -ntl |awk 'NR >2 {print $4}' | grep ^0.0.0.0: |awk -F: '{print $2}' # netstat -ntl |awk 'NR >2 {print $4}' |awk -F: '$0~"^0.0.0.0" {print $2}' --这里用到了字符匹配(后面会讲) 截取/etc/passwd前五行的倒数第二列 # awk -F: 'NR<=5 {print $(NF-1)}' /etc/passwd head -5 /etc/passwd |awk -F: '{print $(NF-1)}' 练习: cat 1.txt 1 2 3 1 2 3 cat 2.txt a b c a b c 要求得到结果 2 a c 2 a c 提示:paste # paste 1.txt 2.txt |awk '{print $2"\t"$4"\t"$6}' 打印第五行 head -5 /etc/passwd |tail -1 awk 'NR==5 {print $0}' /etc/passwd awk '{if (NR==5) print $0}' /etc/passwd 打印第五行的第五列 head -5 /etc/passwd |tail -1 |cut -d":" -f5 awk -F: 'NR==5 {print $5}' /etc/passwd awk -F: '{if (NR==5) print $5}' /etc/passwd 打印第五行和第六行 awk 'NR==5 || NR==6 {print $0}' /etc/passwd awk 'NR>=5 && NR<=6 {print $0}' /etc/passwd 练习: 找出/etc/以.conf结尾的文件的名字(如:kernelcap-2.6.18-164.el5.conf,只需要得到kernelcap-2.6.18-164.el5就可以了) find /etc/ -name "*.conf" |awk -F/ '{print $NF}' |awk -F".conf" '{print $1}' 打印每一行的最后一列(可以以/etc/passwd文件为例,以下相同) # awk -F: '{print $NF}' /etc/passwd 打印每行的字段数 # awk -F: '{print "第"NR"行的字段数为:"NF}' /etc/passwd 打印第五行的字段数 # awk -F: 'NR==5 {print "第"NR"行的字段数为:"NF}' /etc/passwd 打印字段数大于6的行 # awk -F: 'NF>6 {print $0}' /etc/passwd 打印最后一行 # tail -1 /etc/passwd # awk 'END {print $0}' /etc/passwd 打印最后一行的最后一列 # awk -F: 'END {print $NF}' /etc/passwd 打印前五行 # head -5 /etc/passwd # awk 'NR<6 {print $0}' /etc/passwd 打印五到十行,并在前面加上行号 # awk 'NR>=5 && NR<=10 {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 awk -F: '$3==0 {print "管理员有:"$1} $3>0 && $3<500 || $3==65534 {print "系统用户有:"$1} $3>=500 && $3!=65534 {print "普通用户有:"$1}' /etc/passwd 找出每天18:30之前下班的打卡记录 张三 2013-7-01 18:19:28 张三 2013-7-02 17:58:45 张三 2013-7-03 22:41:47 张三 2013-7-04 22:15:23 张三 2013-7-05 19:12:27 张三 2013-7-06 19:03:09 张三 2013-7-07 19:09:44 张三 2013-7-08 19:04:45 张三 2013-7-09 18:39:28 张三 2013-7-10 18:24:48 张三 2013-7-11 18:58:21 张三 2013-7-12 18:36:22 张三 2013-7-13 19:23:46 张三 2013-7-14 19:02:41 张三 2013-7-15 19:00:09 张三 2013-7-16 18:36:13 张三 2013-7-17 18:36:40 张三 2013-7-18 19:00:00 张三 2013-7-19 18:31:18 张三 2013-7-20 18:44:01 张三 2013-7-21 18:37:12 张三 2013-7-22 18:29:33 # awk -F"[: ]*" '$3==18 && $4<30 || $3<18 {print $0} ' 3.txt # awk '$3<"18:30:00" {print $0}' 3.txt --时间直接比较是可以的,但是如果你小时为个位数(比如9:00:00,你应该要写成09:00:00才可以直接比) # awk -F"[ :]*" '$3$4<=1830 {print $0}' 3.txt 假设9点整上班,算出下面这几天这几个人分别迟到多少次,和扣多少钱(一次扣10块) 张三 2013-8-25 09:19:28 李四 2013-8-25 08:58:45 王五 2013-8-25 08:41:47 马六 2013-8-25 09:12:52 田七 2013-8-25 09:01:47 张三 2013-8-24 08:49:28 李四 2013-8-24 08:54:45 王五 2013-8-24 09:11:47 马六 2013-8-24 09:02:52 田七 2013-8-24 09:04:47 张三 2013-8-23 09:29:28 李四 2013-8-23 08:57:45 王五 2013-8-23 08:41:47 马六 2013-8-23 09:08:52 田七 2013-8-23 09:09:47 张三 2013-8-22 09:24:28 李四 2013-8-22 09:16:45 王五 2013-8-22 09:11:47 马六 2013-8-22 08:52:52 田七 2013-8-22 08:44:47 # awk '$3>"09:00:00" {print $0}' 4.txt |awk '{print $1}' |sort |uniq -c |awk 'BEGIN {print "迟到员工姓名\t迟到次数\t应扣金额(元)"} {print $2"\t\t"$1"\t\t"$1*10}' 迟到员工姓名 迟到次数 应扣金额(元) 张三 3 30 李四 1 10 王五 2 20 田七 3 30 马六 3 30 打印所有的列数的总和 --提示:awk是由上往下一行一行的扫描,类似写shell脚本时的循环语句,在这里是自动循环 --思路:先定义一个变量值为0,每扫一行,就加上那一行的列数(NF),最后打印出结果 awk -F: 'BEGIN {sum=0} {sum=sum+NF} END {print sum} ' /etc/passwd 打印列数大于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,并且行数为奇数行的总行数和总列数 awk -F: 'BEGIN {line=0;field=0} NF>5 && NF<8 && NR%2==1 {line=line+1;field=field+NF} END {print "总行数为:"line"\n总列数 为:"field}' /etc/passwd 统计/etc/passwd一共出现了多少次bash字符 (要求用awk) # grep -c bash /etc/passwd # grep -o bash /etc/passwd |wc -l # awk -F"bash" 'BEGIN{sum=0} {sum=sum+NF-1} END {print sum}' /etc/passwd ======================================= 1,监控磁盘使用率,高于80%的找出来,输出警告,发送邮件给管理员 df |awk -F"[ %]*" '1=80{print $1"没容量了"}'|mail -s "fdisk" root df -h |grep -v Filesystem |awk -F% '{print $1}'|awk '$5>80 {print $1"is used "$5"warning!!!"}' |sendmail root df -k |awk '$3/$2>0.8 {print $1"的磁盘使用率为"$3/$2*100"%"}' |mail -s "disk overuse" root df -h |awk -F"[% ]*" '$1~"dev" && $5>80 {print $1"磁盘使用率高于80%,警告!!!"}' |mail -s "disk overuse" root --上面的比较用纯数字比较(使用>=80这种,而不要使用>=80%),因为象100%>=80%得到的结果会为false,也就是说它从第一位开始比较的(因为1小于8) --如果有逻辑卷的分区,那么他会显示两行,你可以使用df -P来显示为一行再来做(在rhel6.5版本中,默认已经是一行了,不是两行) --上面的脚本实际情况你可以放到crontab里,然后做一个判断,如果找出来的为空,则不发邮件;找出来的不为空,那就表示有超过80%使用率的磁盘了,就发邮件;或者是lvm的话,可以做成使用率超过了就自动加空间到LVM分区里 2,监控CPU 15分钟内的平均负载,如果超过5则发警告邮件给管理员 uptime |awk '$NF>5 {print "15分钟内CPU的平均负载超过5"}' | mail -s "warning!" root 3,计算swap总量,使用量,剩余量,使用百分比,剩余百分比 (使用awk) 格式要求为: 总量 使用量 剩余量 使用率 剩余率 xxxxx xxxx xxxx xxx% xxx% # free |tail -1 |awk '{print "总量\t使用量\t剩余量\t使用率\t剩余率"} {print $2"\t"$3"\t"$4"\t"$3/$2*100"%\t"$4/$2*100"%"}' 总量 使用量 剩余量 使用率 剩余率 2047992 0 2047992 0% 100% 4,把系统上ssh的登录成功日志格式化打印成类似下面的 ********************************************************* time hostname user ip port protocol Jul-15 15:47:20 li root 2.2.2.35 6703 ssh2 ********************************************************* ********************************************************* time hostname user ip port protocol Jul-15 15:47:27 li root 2.2.2.35 6704 ssh2 ********************************************************* # cat /var/log/ssh |grep Accepted |awk '{print "***************************************************************************"} {print "时间\t\t主机名\t\t用户名\t客户IP\t\t端口\t协议版本"} {print $1"-"$2,$3"\t"$4"\t\t"$9"\t"$11"\t"$13"\t"$14} {print "****************************************************************************"}' 也可以用下面这种格式,看起来舒服点 时间 主机名头 用户名 IP 端口 协议 Jan-7 17:54:36 server1 root 172.16.2.246 46729 ssh2 Jan-7 17:56:07 server1 root 172.16.2.246 46730 ssh2 Jan-7 18:00:46 server2 root 172.16.2.246 46731 ssh2 Jan-7 18:36:06 server1 root 172.16.2.247 48411 ssh2 Jan-8 00:05:17 server1 root 172.16.2.247 49205 ssh2 Jan-9 14:38:40 li root 172.16.2.35 47666 ssh2 Jan-9 14:40:06 li root 172.16.2.35 47668 ssh2 Jan-9 01:50:36 server1 root 172.16.2.247 44951 ssh2 # cat /var/log/ssh |grep Accepted |awk 'BEGIN {print "时间\t\t主机名\t\t用户名\t客户IP\t\t端口\t协议版本"} {print $1"-"$2,$3"\t"$4"\t\t"$9"\t"$11"\t"$13"\t"$14}' 5,read输入一个目录,精确计算这个目录里所有普通文件(不包括目录)(包括子目录里的文件)的大小之和(要算的是字节总和,不是所有的占用的块空间大小总和) #!/bin/bash read -p "输入一个目录:" dir if [ ! -d $dir ];then echo "不是目录,重试" sh $0 exit 1 fi 方法一: find $dir -type f -exec ls -l {} \; | awk 'BEGIN{sum=0} {sum=sum+$5} END {print sum}' 方法二: find $dir -type f |xargs ls -l | awk 'BEGIN{sum=0} {sum=sum+$5} END {print sum}' 方法三: ls -l `find $dir -type f` | awk 'BEGIN{sum=0} {sum=sum+$5} END {print sum}' 方法四: sum=0 for i in `find $dir -type f` do a=`ls -l $i |awk '{print $5}'` sum=$[$sum+$a] done echo $sum 方法五: ls -lR $dir |grep ^- | awk 'BEGIN{sum=0} {sum=sum+$5} END {print sum}' 6,找出目录下(包括子目录下)的最大的普通文件和最小的普通文件,输出所有普通文件的平均大小 #!/bin/bash read -p "输入一个目录:" dir if [ ! -d $dir ];then echo "不是目录,重试" sh $0 exit 1 fi 方法一: # ll -S -r $dir |awk 'BEGIN {sum=0} NR==2 {print "最小文件为:"$NF" 其大小为:"$5} {sum=sum+$5} END {print "最大文件为:" $NF" 其大小为:"$5"\n平均大小为:"sum/(NR-1)}' 方法二: # find $dir -type f |xargs ls -l |sort -t" " -k5 -n |awk 'BEGIN {sum=0} NR==1 {print "最小文件为:"$NF" 其大小为:"$5} {sum=sum+$5} END {print "最大文件为:" $NF" 其大小为:"$5"\n平均大小为:"sum/NR}'