gawk  -  pattern scanning and processing
       language


awk

调用有三种方式:

1,awk   -F"分隔符"  "command" filename
2,将所有命令插入一个文件,使它权限程序可执行,然后当成脚本调用它
3,是将所有的awk命令插入一个单独文件,然后调用
  awk -f awk-script-file  filename

 

字段的引用
$ 字段操作符
$1代表第一列,$2代表第二列。。。n以此类推
$0代表整个输入记录


比较:
cut -d" "  -f1
sort -t" " -k1
awk -F" " '{print $1}'


比较用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个空格为分隔符


打印所有行    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
[root@dns ~]# awk -F":" '{print $1"的uid是"$3",登录shell 是 "$7}' /etc/passwd

 

$n    n不一定要用整数,也可以用变量值代替
echo a b c |awk 'BEGIN {one=1;two=2} {print $(one+two)}'


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


BEGIN {



 {



END {


[root@dns ~]# awk -F: 'BEGIN {print "这是第一行"} {print $0} END {print "这是最后一行"}' /etc/passwd
这是第一行


用awk打印表格

结构如下:
# echo | awk '{print "第一行"} { print "第二行"} { print "第三行"} {print "第四行"}'
第一行
第二行
第三行
第四行


[root@li ~]# head -1 /etc/passwd  |awk -F: 'BEGIN {print "*************"} {print "*   "$1"    *"}{print "*   "$2"    *"}{print "*    "$3"   *"}{print "*    "$4"    *"}{print "*    "$5"    *"}{print "*    "$6"    *"}{print "*    "$7"    *"} END  {print "*************"}'

*************
*   root    *
*   x    *
*    0   *
*    0    *
*    root    *
*    /root    *
*    /bin/bash    *
*************
--结果的对齐方面可以自己再去调整

 


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

--awk  内置变量

FS   设置分隔符,等同于-F
NF   代表字段数
$NF  代表最后一个字段数
NR   代表当前处理第几行


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


 netstat -ntl |grep -v Active| grep -v Proto|awk '{print $4}'|awk -F: '{print $NF}'

netstat -ntlup |grep -Ev "Active|Proto" |awk '{print $4}' |awk -F: '{print $NF}'


grep增强模式
查找/etc/passwd里包含root或daemon的行
cat /etc/passwd |grep -E "(root|daemon)"
查找/etc/passwd里包含root或daemon或adm的行
cat /etc/passwd |egrep "(root|adm|daemon)"


回顾上次的一个题目,把/etc下所有的.conf结尾的文件拷到一个目录,再改为.html结尾;不用rename,用awk来截

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

 

打印第五行
head -5 /etc/passwd |tail -1
awk '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: '{print $NF}' /etc/passwd
  或者awk  '{FS=":"} {print $NF}' /etc/passwd  --第一行会打印出整行

打印第五行awk 'NR==5 {print $0}' /etc/passwd
  或者 awk '{if (NR==5) print $0}' /etc/passwd

打印每行的字段数 awk  -F: '{print NF}' /etc/passwd
打印第五行的字段数  awk  -F: 'NR==5 {print NF}' /etc/passwd

打印最后一行awk -F: ' END {print $0} ' /etc/passwd

打印最后一行的最后一列 awk -F: ' END {print $NF} ' /etc/passwd
  或者:awk -F: '{a=$NF}END {print a} ' /etc/passwd


awk关系操作符
==   等于
!=    不等于
>    大于
<    小于
>=   大于等于
<=   小于等于


awk逻辑操作符
&& 逻辑与
| | 逻辑或
! 非


awk运算操作符
+ - * / %
^  幂   比如:2的三次方  2^3   --/bin/bash里面求幂为2**3

 

打印前五行
 awk -F: 'NR<=5 {print $0}' /etc/passwd


打印五到十行,并在前面加上行号
 # awk -F: 'NR>=5 && NR<=10 {print NR,$0}' /etc/passwd


打印奇数行 (删除偶数行)
awk -F: 'NR%2==1 {print NR,$0}' /etc/passwd


打印偶数行 (删除奇数行)
awk -F: 'NR%2==0 {print NR,$0}' /etc/passwd


删除五分之一的行(打印其五分之四的行,要求这五分之一的行号间隔平均)
awk -F: 'NR%5!=1 {print NR,$0}' /etc/passwd

 

打印/etc/passwd里普通用户的行
awk -F: '$3>=500 && $3!=65534 {print $0}' /etc/passwd

 

打印字段数大于6的行
awk -F: 'NF>6 {print $0}' /etc/passwd

 

打印所有的列数的总和
--提示:awk是由上往下一行一行的扫描,类似写shell脚本时的循环语句,在这里是自动循环
--思路:先定义一个变量值为0,每扫一行,就加上那一行的列数(NF),最后打印出结果

# awk -F: 'BEGIN {a=0} {a=a+NF} END {print a}' /etc/passwd


打印列数大于5的总行数

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


综合运算  echo |awk '{print 10^2+(90-80)}'

也可以进行浮点数运算 echo |awk '{print 10.9^2+(90.5-80.2)/3}'

 


例1:监控磁盘使用率,高于80%的找出来,输出警告,发送邮件给管理员


df -h |grep -v 容量|awk '{print $1,$5}'|awk -F% '{print $1}' |while read  a
do
                used=`echo $a| awk '$2>80 {print $1" is used:" $2"% warning!!!!!!!"}'`
                echo $used >> /tmp/used.txt
done
mail root  < /tmp/used.txt

 

df -l |grep -v Filesystem |awk '{ if ($3*100/$2>80) print $1"使用率超过80%"}' | mail root

 

例2:计算swap总量,使用量,剩余量,使用百分比,剩余百分比

格式要求为:
total used free used% free%
xxxxx xxxx xxxx xxx% xxx%

--提示:
[root@dns shell04]# echo | awk '{print "第一行"} { print "第二行"} { print "第三行"}''
第一行
第二行
第三行

# free |grep Swap |awk '{print "total\tused\tfree\tused%\tfree%"} {print $2"\t"$3"\t"$4"\t"$3*100/$2"%\t"$4*100/$2"%"} '
total   used    free    used%   free%
4096564 0       4096564 0%      100%

 

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


--awk外部脚本

脚本的结构

BEGIN {
}
      {
}

END {
}

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


BEGIN {
        FS=":"
}
        {
        print $1
}

# awk -f 1.awk /etc/passwd 

 

例:  打印字段数大于5的总行数  (用脚本写)
awk -F: 'BEGIN {c=0} {if (NF>5) {c=c+1}} END {print c}' /etc/passwd

BEGIN {
     FS=":"
     c=0
}

{
         NF>5     --这两句用if来写的话  为:  if ( NF>5 )
        { c=c+1 }            c=c+1 
}

END {
        print c
}


例:找出目录下的最大的文件和最小的文件,输出平均大小
思路: ls 有一个参数大写字母S,会把文件从大到小排序,排序后,最大文件就是第一行(NR=1),最小文件就是最后一行,平均大小为(累计总大小/NR);注意 grep -v 总计 这句如果是英文系统,就换成grep -v Total

vim 3.awk

BEGIN   {
        total=0
}
        {
        if (NR==1)
        print "最大文件是:"$NF" 其大小为:"$5
        total=total+$5
}
END     {
        print "最小文件是:"$NF" 其大小为:"$5
        print "所有文件平均大小为:"total/NR

}

 

# ll -S |grep -v total |awk -f 3.awk 最大文件是:shell04.txt 其大小为:7245
最小文件是:1.awk 其大小为:33
所有文件平均大小为:2957.8

 

-------------------------------

--awk字符匹配

==  完全精确匹配
~ 匹配
!~      不匹配

完全匹配
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

 

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

--非awk的用法,两条命令
cat /etc/passwd |grep nologin$ |cut -d":" -f1  --打印
cat /etc/passwd |grep nologin$ |cut -d":" -f1 |wc -l --统计个数

 
# cat /etc/passwd |awk -F: 'BEGIN {a=0} { if ($7=="/sbin/nologin") {print $1; a=a+1} } END {print "其总个数为"a}'

 


例:倒序排序所有字段(/etc/passwd)

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
可以看到结果倒序排列

 

 

 


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

 

sed  流编辑器

  sed  - stream editor for filtering and trans-
       forming text


删除

head -n 5 /etc/passwd |cat -n |sed -e '2d' --指定删除第二行

head -n 5 /etc/passwd |cat -n |sed -e '2,3d'  --删除第二行到第三行,中间为逗号,表示范围

head -n 5 /etc/passwd |cat -n |sed -e '1d;5d' --删除第一行和第五行,中间为分号,表示单独的操作

head -n 5 /etc/passwd |cat -n |sed -e '1d;5d;3d'

head -n 5 /etc/passwd |cat -n |sed -e '1d;3,5d'


--利用正则表达式来匹配行
head -n 5 /etc/passwd |cat -n |sed -e '/oo/d' --删除匹配oo的行

head -n 5 /etc/passwd |sed -e '/^root/d' --删除以root开头的行

head -n 5 /etc/passwd |sed -e '/bash$/d' --删除以bash结尾的行

cat /etc/inittab |sed -e '/^$/d' --删除空格行

head -n 5 /etc/passwd |sed -e '/^[a-z]/d'

head -n 5 /etc/passwd |sed -e '/^[a-z]/d' --删除小写字母开头的行,用[a-z]表示

head -n 5 /etc/passwd |sed -e '/^[A-Z]/d'--删除大写字母开头的行,用[A-Z]表示

head -n 5 /etc/passwd |sed -e '/^[a-Z]/d' --删除以字母开头的行,用[a-Z]表示,小写的a到大写的Z

head -n 5 /etc/passwd |sed -e '/^[[:alpha:]]/d' --也是删除以字母开头的

head -n 5 /etc/passwd |sed -e '/^[[:digit:]]/d' --删除以数字开头的

head -n 5 /etc/passwd |sed -e '/^[[:upper:]]/d' --删除以大写字母开头的

head -n 5 /etc/passwd |sed -e '/^[[:lower:]]/d'  --删除以小写字母开头的

head -n 5 /etc/passwd |sed -e '/^[[:punct:]]/d' --删除以标点符号开头的

head -n 5 /etc/passwd |sed -e '/^[[:blank:]]/d' --删除以空格开头的
head -n 5 /etc/passwd |sed -e '/^ /d' --同上
head -n 5 /etc/passwd |sed -e '/^\ /d' --同上


cat /etc/inittab |sed '/^#/d;/^$/d'
 --删除/etc/inittab的空行和注释


yum install smb* -y
 /etc/samba/smb.conf


yum install vsftpd* -y
/etc/vsftpd/vsftpd.conf

yum install sendmail* -y
/etc/mail/sendmail.mc  --以dnl开头来注释的


对源文件进行操作需要  -i 参数

 

练习:sed -i 删除vsftpd.conf,smb.conf,sendmail.mc里所有的注释和空格
# sed -i '/^#/d;/^$/d' /etc/vsftpd/vsftpd.conf
# sed -i '/#/d;/^$/d;/^;/d' /etc/samba/smb.conf
# sed -i '/^dnl /d' /etc/mail/sendmail.mc

 

打印

head -5 /etc/passwd |sed -e '/root/p' --匹配root的行打印出来,但发现/etc/passwd的前五行也打印出来了

head -5 /etc/passwd |sed -n -e '/root/p' --去除常规打印的信息,只打印匹配的行,要多加一个-n的参数,注意:-n不能加在-e之后,用-ne也可以,但用-en不行

head -5 /etc/passwd |sed -ne '/^root/p'

head -5 /etc/passwd |sed -ne '/nologin$/p'

cat /etc/inittab |sed -ne '/^$/p'

head  -5 /etc/passwd |sed -ne '1,4p'

head  -5 /etc/passwd |sed -ne '1p;4p'

head  -5 /etc/passwd |sed -ne '/^[[:upper:]]/p;/^[a-z]/p' --分别打印大写字母开头的行和小写字母开头的行

head  -5 /etc/passwd |sed -ne '/^[[:upper:]]/,/^[a-z]/p' --用正则表达式实现范围打印

head  -5 /etc/passwd |sed -ne '/^[[:upper:]]/,/nologin$/p'


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

 

格式化输入所有ssh登录成功的信息

[root@li shell04]# sed -n '/Accepted.*from/p' /var/log/secure | awk '{print "*********************************************************"} {print "time\t\thostname\tuser\tip\t\tport\tprotocol"} {print $1"-"$2" "$3"\t"$4"\t\t"$9"\t"$11"\t"$13"\t"$14} {print "*********************************************************"}'
*********************************************************
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
*********************************************************

 

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


替换

head  -5 /etc/passwd |sed -e 's/root/sed/'
head  -5 /etc/passwd |sed -e 's/:/@_@/' --把每一行的第一个匹配的字符替换

head  -5 /etc/passwd |sed -e 's/:/@_@/g' --后面加一个g,代表全替换

head  -5 /etc/passwd |sed -e 's/:/&@_@&/g' --&符号代表前面匹配的字符串

head  -5 /etc/passwd |sed -e '2,4s/:/&@_@&/g' --指定替换2到4行

 head  -5 /etc/passwd |sed -e '1,$s/:/&@_@&/g' --替换所有行,默认不加的话就是替换所有行

head  -5 /etc/passwd |sed -e '/^root/,/^adm/s/:/&@_@&/g' --使用正则表达式来表示替换的行范围

head  -5 /etc/passwd |sed -e '/^root/s/:/@_@/g;/^adm/s/:/@_@/g'  --替换以root开头的行和替换以adm开头的行

 

练习:
假如一台电脑开机后自动获取IP

写一个脚本
要求:
1,假如获取的IP为2.2.2.X,把其以后都改为静态固定获取这个IP
2,自动把它的主机名改为stationX.cluster.com(X代表获取IP的最后一个数),并加到/etc/hosts里,还要修改/etc/sysconfig/network里的全局主机名;
3,修改ssh端口为2222,日志记录到local5设备,并记录到/var/log/ssh
4,并配置/var/log/ssh每月1号0点轮转一次
5,每月最后一天23点41分对成功登录的ssh信息打印成表的形式并保留成/tmp/ssh_success.txt文件,类似下面的表形式

*********************************************************
time            hostname        user    ip              port    protocol
Jul-15 15:47:27 li              root    2.2.2.35        6704    ssh2
*********************************************************


#!/bin/bash


ip=`ifconfig eth0 |grep Bcast|awk '{print $2}'|awk -F: '{print $2}'`
iptail=`echo $ip |awk -F. '{print $NF}'`

sed -i '/^BOOTPROTO/s/dhcp/static/' /etc/sysconfig/network-scripts/ifcfg-eth0
echo "IPADDR=$ip" >> /etc/sysconfig/network-scripts/ifcfg-eth0
echo "NETMASK=255.255.255.0" >> /etc/sysconfig/network-scripts/ifcfg-eth0


hostname station"$iptail".cluster.com
echo "$ip station$iptail.cluster.com" >> /etc/hosts

sed -i '/^HOSTNAME/d' /etc/sysconfig/network
echo "HOSTNAME=station$iptail.cluster.com" >> /etc/sysconfig/network

 


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


使用组或者域进行打印的定位
\(\)是将\( 和 \) 之间的字符串定义为组,并且将匹配这个表达式的保存到一个区域(一个正则表达式最多可以保存9个),它们使用\1到\9来表示


或者使用扩展模式() 直接把括号内的字符串定义为组

# echo "hi,Mr smith.welcome" |sed -e 's/\(.*\),\(.*\)\.\(.*\)/\3,\2,\1/'
welcome,Mr smith,hi

# echo "hi,Mr smith.welcome" |sed -r 's/(.*),(.*)\.(.*)/\3,\2,\1/'
welcome,Mr smith,hi
--使用-r参数使

用扩展模式,在分域时可以去掉\符号,看上去比较简洁


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

将/etc/passwd的第一行进行分域反转
[root@li www]# head -1 /etc/passwd | sed -r 's/(.*):(.*):(.*):(.*):(.*):(.*):(.*)/\7:\6:\5:\4:\3:\2:\1/'
/bin/bash:/root:root:0:0:x:root


把/etc/passwd上下进行反转
# cat -n /etc/passwd |sort -t" " -k 1 -n -r |sed -r 's/(.......)(.*)/\2/'

 


删除每行的第一个字符
# head -5 /etc/passwd | sed -r 's/(.)(.*)/\2/'


删除每行的第二个字符
head -5 /etc/passwd |sed -r 's/(.)(.)(.*)/\1\3/'


删除每行的第九个字符
head -5 /etc/passwd |sed -r 's/(........)(.)(.*)/\1\3/'


删除倒数第5个字符
head -5 /etc/passwd |sed -r 's/(.*)(.)(....)/\1\3/'

 

把每行的第5个字符和第8个字符互换,并删除第10个字符
head -5 /etc/passwd |sed -r 's/(....)(.)(..)(.)(.)(.)(.*)/\1\4\3\2\5\7/'

 

正则达式补充:
a+代表多个a
[a-z]+代表多个小写字母
[A-Z]+代表多个大写字母
[a-Z]+代表多个字母


删除每行的第一个单词
cat /etc/fstab |sed -r 's/([a-Z]+)(.*)/\2/'

cat /etc/fstab |sed -r 's/([a-Z]+)([^a-Z]+)(.*)/\2\3/'


删除每行的第二个单词
cat /etc/fstab |sed -r 's/([a-Z]+)([^a-Z]+)([a-Z]+)(.*)/\1\2\4/'

 

删除每行的最后一个单词
cat /etc/fstab |sed -r 's/(.*)([^a-Z]+)([a-Z]+)([^a-Z]+$)/\1\2\4/'


删除每行的倒数第二个单词
cat /etc/fstab |sed -r 's/(.*)([^a-Z]+)([a-Z]+)([^a-Z]+)([a-Z]+)([^a-Z]+$)/\1\2\4\5\6/'


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

学习完shell后你应该可以写出下列几大类脚本:

触发型脚本
自动安装型
监控警告型
自动管理型
日志处理型
自动应答型


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

 

对apache的日志进行处理

笔记目录下有一个access.log日志文件
处理前格式为:
10.1.1.183 - - [02/Apr/2011:15:36:54 +0800] "GET /shell05/shell05.txt HTTP/1.1" 200 6305 "http://10.1.1.35/shell05/" "Mozilla/5.0 (X11; U; Linux i686; zh-CN; rv:1.9.0.12) Gecko/2009070811 Red Hat/3.0.12-1.el5_3 Firefox/3.0.12"


处理后格式为:
时间            访问ip         访问文件 
2011-apr-02 15:36:54     10.1.1.183      shell05.txt

 

cat access_log  |awk '{print $4"\t"$1"\t"$7}'| awk -F[ '{print $2}'| sed -e 's/:/ /g'|sed -e 's/\// /g'|awk '{print $3","$2","$1","$4":"$5":"$6","$7","$NF}'|sed -r 's/(.*),(.*),(.*),(.*),(.*),(.*)/\1-\2-\3 \4\t\5\t\6/'