shell 脚本编程
####################################
回顾:
sed 文本编辑器,逐行处理,非交互
sed [选项] ‘条件指令’ 文件
选项:
-n 屏蔽默认输出
-i 修改原文件
-r 支持扩展正则
条件:行号,/正则/
指令:增删改查
a 行前面写入
i 行后面写入
d 删除行 dd
s s/旧// 变相删除
c 修改行3c /root/c
s s/旧/新/
p 打印、输出
r 导入
w 导出
####################################
sed 复制剪切: H 追加 复制 ; h 覆盖 复制
G 追加 粘贴 ; g 覆盖 粘贴
sed -n ‘3h;6g’ a.txt 复制第3行内容覆盖插入到第6行 (原本的第6行没了)
sed -n ‘3h;6G’ a.txt 复制第3行内容插入到第6行下面 (原本的第6行还在)
sed -n ‘3H;6g’ a.txt 复制第3行内容覆盖插入到第6行并先空一行(原本的第6行没了)
sed -n ‘3H;6G’ a.txt 复制第3行内容插入到第6行下面并先空一行 (原本的第6行还在)
####################################
一、awk基本用法
1.awk命令解析
1)主要用法
awk 功能与grep类似,比grep功能多
awk 原理和语法格式与sed类似
awk [选项] ‘条件{指令}’ 文件
多条命令用分号隔开;print是最常用的指令;
没有指令默认为打印全部符合条件的内容
#######################################
例如:取每一行的第三个字:awk ‘{print $3}’ a.txt
输出内存大小(注意内存会实时变化):free | awk ‘/Mem/{print $4}’
提取根分区剩余容量df;内存剩余容量free;cpu负载uptime(最近1、5、15分钟负载,相当于top的第一行)(linux允许超负载);网卡流量ifconfig(RX 发送包,TX接受包);
num=`df | awk '//$/{print $4}'` (以根结尾的)
df -hT / | tail -1 | awk '{print $6}'
free | awk '/Mem/{print $4}'
uptime | awk '{print $10}'
ifconfig enp2s0 | awk ‘/pack/{print $5}’
==ifconfig enp2s0 | grep "inet " | awk '{print $2}'
#######################################
网页实时更新根分区剩余容量、内存剩余容量、CPU负载、网卡流量等
yum -y install httpd systemctl restart/enable
vim a.txt
echo 'Content-Type:text/html'
echo ''
echo ''
echo "根分区剩余容量为:"
df | awk '//$/{print $4}'
echo "</br>"
echo "内存剩余容量为:"
free | awk '/Mem/{print $4}'
echo "</br>"
echo "CPU负载为:"
uptime | awk '{print $8,$9,$10}'
echo "</br>"
echo "网卡流量为:"
ifconfig enp2s0 | awk '/pack/{print $5}'
echo "</br>"
脚本存放位置:/var/www/cgi-bin ,给个x权限,每刷新下网页脚本就会运行下;关闭selinux;刷新网页F5;
firefox http://172.40.55.11x/cgi-bin/test.sh
#######################################
筛选不以空格分隔的文档
-F :指定分隔符(不写默认为空格或tab键)
指定分隔符为冒号:
awk -F: '/^roo/{print $1,$2,$3,$4,$5}' /etc/passwd
awk的内置变量:
$0 一整行(全文)
$1 第1列 ; $2 第2列 ... ...
NF 当前行有几列,逐行处理(列数)
NR 当前行的行号(行数)
FNR 记录当前处理行在原文本内的序号(行号)
FS 保存或设置分隔符,FS=“:”
awk '{print NF}' a.txt
#######################################
tailf /var/log/secure
回车<屏幕变空白>
其他主机:ssh 该虚拟机 <输入错误密码>
对公网开放防止远程登录及破解密码:找出登录失败的ip,并放入防火墙
while :
do
if 在里面就不再导了
awk ‘/Failed/{print $11}’ /var/log/secure | grep -v “[a-Z]” >>ip.txt
for i in $(cat ip.txt)
do
加到防火墙
done
sleep 300
done
#######################################
支持直接常量,常量变量混搭:
awk -F: ‘{print “hello”,$1}’ /etc/passwd /这个文件有多少行就输出多少次,双引号,hello+用户名
awk 完整语法:可以单独使用,也可以一起使用
awk ‘BEGIN{} 条件{} END{}’ 文件
begin{} 后面的命令,在读取文件内容之前执行,只执行一次 /初始化
条件{} 读取文件的过程中执行,执行n次(文件的行数) /处理
END{} 读取文件的过程后执行,只执行一次 /总结
awk ‘BEGIN{i=0} /[0-9]/{i++} END{print i}’ 文件
awk 'BEGIN{x=0}/<bash$/{x++} END{print x}' /etc/passwd
== egrep -c '<bash$' /etc/passwd /-c统计匹配的行数
awk ‘BEGIN{x=1.2;print x/2} /可以做算术运算
awk ‘BEGIN{print 1000/2} /可以常量或变量
#######################################
分析能登录计算机的用户有多少?(有bash解释器)
awk ‘BEGIN{i=0} /bash$/{i++} END{print i}’ /etc/passwd
awk的变量不用定义,可以直接调用。默认字串:空;初始数值:0
awk ‘ /bash$/{i++} END{print i}’ /etc/passwd
#######################################
用户名 UID Shell
root 0 /bin/bash
user1 500 /sbin/nologin
总共用户量:xx ( tab键)
awk -F ‘BEGIN{print “用户名 UID shell”} /bash$/{print $1” ”$3” ”$7;x++} END{print “总共用户量:”,x}’ /etc/passwd
awk -F: 'BEGIN{print "User UID Home"} {print $1" "$3" "$6} END{print "Total"NR"lines."}' /etc/passwd
#######################################
(UID/GID范围: cat /etc/login.defs ; ~包含 ;)
条件的表现形式:正则表达式、数值/字符串比较、逻辑比较、运算符
模糊匹配:awk -F: ‘$1~/root/{print $1}’ /仅对第一列匹配(正则包含即可)
精确匹配:awk -F: ‘$1==”root“{print $1}’
精确匹配:awk -F: ‘$3>=1000{print $1}’
NR==2 当前行为第二行
NF>=2 包含两个及以上字段的行
awk -F: ‘$3>1000||$3<10’ /etc/passwd /支持&&和||
#######################################
练习:ftp://172.40.50.118/share/shell/SHELL/
awk_exam.pdf grade.txt
显示不包含 Brown 的行
awk '$0 !~ /Brown/' grade.txt
当第一列的第四个字符是 a 时,显示该行
awk '$1 ~/^...a/' grade.txt
当第六列大于第七列是,显示$6 大于 $7
awk '$6 > $7 {print $6,"大于",$7}' grade.txt
显示包含 Yellow 或 Brown 的行
awk '$0 ~/(Yellow|Brown)/' grade.txt awk '$0 ~/Yellow/||/Brown/' grade.tx
显示文档第一列、第六列、第七列,如果第一列是 M.Tansley,则先将其第六列的值减 1 后再输出
awk '{if($1=="M.Tansley") $6=$6-1; print $1,$6,$7}' grade.txt
awk /San/ data.txt 显示包含San的行
列出UID间于501~505的用户详细信息
# awk -F: ‘($3>=501)&&($3<=505)’ /etc/passwd
输出/etc/hosts文件内以127或192开头的记录
# awk '/^(172|127)/' /etc/hosts 或 awk '/^127/||/^172/' /etc/hosts
列出100以内整数中7的倍数或是含7的数
# seq 100 | awk ‘/7/||$1%7==0’
seq 100 | awk 'NR%7==0||NR~/7/'
seq 100 | awk '$0%7==0||$0~/7/'
seq 100 | awk '$1%7==0||$1~/7/'
#######################################
综合应用
找到使用bash作登录Shell的本地用户
1.从passwd里把能登录的用户提取出来
2.从shadow里把他们的密码取出来
#!/bin/bash
user=`awk -F: ‘/bash$/{print $1}’ /etc/passwd` /注意反引号
for i in $user
do
awk -F: -v x=$i ‘$1==x{print $1,$2}’ /etc/shadow
done
-v 调用外部shell变量
awk -v x=10 ‘{print x}’ /etc/hosts /加文件以逐行处理
#######################################
awk -F: '{print $1","$7}' passwd.txt
输出第3行(行号NR等于3)的用户记录:awk -F: 'NR==3' passwd.txt
输出奇数行(行号NR除以2余数为1)的用户记录:awk -F: 'NR%2==1' passwd.txt
输出前3行文本:awk -F: 'NR<=3' passwd.txt
输出从第5行开始到文件末尾的所有行:awk -F: 'NR>=5' passwd.txt
输出UID小于3或者UID是偶数的用户记录: awk -F: '$3<=3||$3%2==0' passwd.txt
输出当前用户的用户名、宿主目录、登录Shell信息:
awk -F: '$1==ENVIRON["USER"]{print $1,$6,$7}' passwd.txt
#######################################
day07
awk:编程语言
回顾:
awk数据过滤软件(grep)
处理列【条件:/正则/,字符,数字,&&,||】
awk ‘BEGIN{} 条件{} END{}’ 文件
== != >= > < <=
-eq -gt -ge shell
awk -F: ‘/root/{print $1,$3}’ passwd
awk -F: ‘$1==’root’{print $1,$3}’ passwd
#######################################
awk的if语句
if(判断){}
if(){} else{} ; if(){} else if(){} else{}
awk ‘BEGIN{if..} 条件{if..} END{if..}’ 文件
awk对空格没严格要求,不识别单引号
统计/etc/passwd文件中UID小于或等于500的用户个数:
awk -F: 'BEGIN{i=0}{if($3<=500){i++}}END{print i}' /etc/passwd
分别统计/etc/passwd文件中UID小于或等于500、UID大于500的用户个数:
awk -F: 'BEGIN{i=0;j=0} { if($7~/bash$/){i++} else{j++} } END{print i,j}' /etc/passwd
分别统计/etc/passwd文件中登录Shell是“/bin/bash”、“/sbin/nologin”、其他的用户个数:
awk -F: 'BEGIN{i=0;j=0;k=0} {if($7~/bash$/){i++}else if($7~/nologin$/){j++}else{k++}} END{print i,j,k}' /etc/passwd
#######################################
awk的while循环
while(){}
awk ‘BEGIN{while..} 条件{while..} END{while..}’ 文件
求词频:
以“:”或“/”做分隔,针对每一行的每一列进行比对,如果包含“root”,则次数加1。其中,逐行处理直接由awk完成,逐列处理交给while循环,通过i变量依次取$1、$2、……、$NF进行检查;变量j在预处理时赋值0,没匹配一个字段加1。
awk -F [:/] 'BEGIN{j=0}{i=1} {while(i<=NF){if($i~/root/){j++};i++}} END{print j}' /etc/passwd
awk -F [:/] ' {i=1; while(i<=NF){if($i~/root/){j++};i++}} END{print j}' /etc/passwd
echo $(cat /etc/passwd) | awk -F "root" '{print NF-1}'
#######################################
awk的数组、for循环
awk的下标可以是数字或字串;awk的下标可以不定义;
awk {a[0]=11;a[2]=88; print a[0],a[2]}
awk {a[nihao]=11;a[chen]=88; print a[0],a[2]}
公式:(除$1外,其他都不变)
awk '{ip[$1]++}’ /IP[$1]数组变量
who | awk '{ip[$1]++} END{for(i in ip) {print i,ip[i]}}' /看用户登录情况
awk '{ip[$1]++} END{for(i in ip) {print i,ip[i]}}' /var/log/httpd/access_log
分析Web日志的访问量排名,要求获得客户机的地址、访问次数,并且按照访问次数排名
针对文本排序输出可以采用sort命令,相关的常见选项为-r、-n、-k。其中-n表示按数字顺序升序排列,而-r表示反序,-k可以指定按第几个字段来排序。
awk '{ip[$1]++} END{for(i in ip) {print i,ip[i]}}' /var/log/httpd/access_log | sort -nr -k 2
#######################################
准备工作:
服务端:(真机)
yum -y install httpd
systemctl start httpd
ls -lh /var/log/httpd/access_log
wc -l /var/log/httpd/access_log
curl http://192.168.4.254/ /自己访问自己
客户机:
yum -y install httpd-tools
ab -c 100 -n 10000 http://192.168.4.254/ /ab最大并发2万次
DOS攻击【deny of service】
回服务端:(svr7)
vim /var/log/httpd/access_log
wc -l /var/log/httpd/access_log /对比
ls -lh /var/log/httpd/access_log /对比字节
#######################################
NSD SHELL DAY06
1 案例1:使用awk提取文本
1.1 问题
本案例要求使用awk工具完成下列过滤任务:
练习awk工具的基本用法
提取本机的IP地址、根分区使用率
格式化输出/etc/passwd文件中的用户名、UID、宿主目录信息
格式化输出passwd文件内容时,要求第一行为列表标题,最后一行提示一共已处理文本的总行数,如图-1所示。
图-1
1.2 步骤
实现此案例需要按照如下步骤进行。
步骤一:awk文本过滤的基本用法
1)基本操作方法
格式:awk [选项] '[条件]{编辑指令}' 文件
其中,print 是最常用的编辑指令;若有多条编辑指令,可用分号分隔。
处理文本时,若未指定分隔符,则默认将空格、制表符等作为分隔符。
直接过滤文件内容:
[root@svr5 ~]# awk '{print $1,$2}' /etc/rc.local //输出文件的第1、2列
#!/bin/sh
#
# This
# You
# want
touch /var/lock/subsys/local
结合管道过滤命令输出:
[root@svr5 ~]# uname -a //正常的完整输出
Linux svr5.tarena.com 2.6.18-348.el5 #1 SMP Wed Nov 28 21:22:00 EST 2012 x86_64 x86_64 x86_64 GNU/Linux
[root@svr5 ~]# uname -a | awk '{print $1,$3,$12}' //输出第1、3、12字段
Linux 2.6.18-348.el5 x86_64
2)选项 -F 可指定分隔符
截取/etc/passwd文件的前7行,用来创建一个测试文件,操作如下:
[root@svr5 ~]# head -7 /etc/passwd > passwd.txt
[root@svr5 ~]# cat passwd.txt
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
输出passwd.txt文件中以分号分隔的第1、7个字段,显示的不同字段之间以逗号隔开,操作如下:
[root@svr5 ~]# awk -F: '{print $1","$7}' passwd.txt
root,/bin/bash
bin,/sbin/nologin
daemon,/sbin/nologin
adm,/sbin/nologin
lp,/sbin/nologin
sync,/bin/sync
shutdown,/sbin/shutdown
或者:
[root@svr5 ~]# awk -F ":" '{print $1","$7}' passwd.txt
root,/bin/bash
bin,/sbin/nologin
daemon,/sbin/nologin
adm,/sbin/nologin
lp,/sbin/nologin
sync,/bin/sync
shutdown,/sbin/shutdown
awk还识别多种单个的字符,比如以“:”或“/”分隔,输出第1、10个字段:
[root@svr5 ~]# awk -F [:/] '{print $1,$10}' passwd.txt
root bash
bin nologin
daemon nologin
adm sbin
lp
sync sync
shutdown shutdown
输出每次处理的行号,以及当前行以“:”分隔的字段个数:
[root@svr5 ~]# awk -F: '{print NR,NF}' passwd.txt
1 7
2 7
3 7
4 7
5 7
6 7
7 7
3)awk处理的时机
awk会逐行处理文本,支持在处理第一行之前做一些准备工作,以及在处理完最后一行之后做一些总结性质的工作。在命令格式上分别体现如下:
行前处理,BEGIN{ }
逐行处理,{ }
行后处理,END{ }
上述编辑指令段可以包含在一对单引号内,比如:
awk [选项] ' BEGIN{编辑指令 } {编辑指令} END{编辑指令}' 文件
只做预处理的时候,可以没有操作文件,比如:
[root@svr5 ~]# awk 'BEGIN{A=1024;print A*2.56}'
2621.44
举个包括三个处理时机的例子——“统计系统中使用bash作为登录Shell的用户总个数:预处理时赋值变量x=0,然后逐行读入 /etc/passwd文件检查,如果发现登录Shell是/bin/bash则x增加1,全部处理完毕后,输出x的值即可。相关操作及结果如下:
[root@svr5 ~]# awk 'BEGIN{x=0}/<bash$/{x++} END{print x}' /etc/passwd
29
当然,这个例子比较简单(效果与egrep -c '<bash$' /etc/passwd 相同),此处仅仅是用来说明awk三种处理时机的用法。在实际工作中,利用awk的这种处理流程可以完成许多更复杂的任务。
步骤二:利用awk提取本机的IP地址、根分区使用率
1)提取IP地址
分步实现的思路及操作参考如下——
通过ifconfig eth0查看网卡信息,其中包括IP地址:
[root@svr5 ~]# ifconfig eth0
eth0 Link encap:Ethernet HWaddr 00:0C:29:82:09:E9
inet addr:192.168.4.4 Bcast:192.168.4.255 Mask:255.255.255.0
inet6 addr: fe80::20c:29ff:fe82:9e9/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:358524 errors:0 dropped:0 overruns:0 frame:0
TX packets:230638 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:44470760 (42.4 MiB) TX bytes:64236279 (61.2 MiB)
结合grep获得包含IP地址的那一行:
[root@svr5 ~]# ifconfig eth0 | grep "inet addr:"
inet addr:192.168.4.4 Bcast:192.168.4.255 Mask:255.255.255.0
再结合awk过滤出默认分隔的第2列:
[root@svr5 ~]# ifconfig eth0 | grep "inet addr:" | awk '{print $2}'
addr:192.168.4.4
进一步结合awk过滤出以“:”分隔的第2列:
[root@svr5 ~]# ifconfig eth0 | grep "inet addr:" | awk '{print $2}' | awk -F: '{print $2}'
192.168.4.4
2)提取根分区使用率
分步实现的思路及操作参考如下——
通过df命令查看根分区的使用情况,其中包括使用率:
[root@svr5 ~]# df -hT /
文件系统 类型 容量 已用 可用 已用% 挂载点
/dev/sda2 ext3 19G 7.2G 11G 40% /
输出上述结果中最后一行的第6列:
[root@svr5 ~]# df -hT / | tail -1 | awk '{print $6}'
40%
步骤三:格式化输出/etc/passwd文件
1)任务需求及实现思路分析
根据任务要求的结果,输出的内容包括三个部分:列表头、用户信息、列表尾。
由于/etc/passwd文件中的用户记录是单一的以“:”分隔,而且恰好awk本身就已经支持“前、中、后”三段式处理,所以用awk处理是再合适不过了。通过awk的内置变量NR即可获得处理的记录行数,因此只要设置正确的输出即可。
2)根据实现思路编写、验证awk过滤语句
输出信息时,可以使用“ ”显示Tab制表位:
[root@svr5 ~]# awk -F: 'BEGIN{print "User UID Home"}
{print $1" "$3" "$6} END{print "Total "NR" lines."}' /etc/passwd
User UID Home
root 0 /root
bin 1 /bin
daemon 2 /sbin
adm 3 /var/adm
lp 4 /var/spool/lpd
sync 5 /sbin
.. ..
iamkiller 1234 /opt/.private/iamkiller
nsd001 0 /home/nsd001
nsd002 1236 /home/nsd002
nsd003 1237 /home/nsd003
postfix 89 /var/spool/postfix
Total 67 lines.
2 案例2:awk处理条件
2.1 问题
本案例要求使用awk工具完成下列过滤任务,注意awk处理条件的设置:
列出UID间于501~505的用户详细信息
输出/etc/hosts文件内以127或192开头的记录
列出100以内整数中7的倍数或是含7的数
2.2 步骤
实现此案例需要按照如下步骤进行。
步骤一:认识awk处理条件的设置
创建测试文件passwd.txt文件:
[root@svr5 ~]# head -7 /etc/passwd > passwd.txt
[root@svr5 ~]# cat passwd.txt
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
1)使用正则表达式设置条件
输出其中以bash结尾的完整记录:
[root@svr5 ~]# awk -F: '/bash$/{print}' passwd.txt
root:x:0:0:root:/root:/bin/bash
输出以a、b、c或d开头的用户名、宿主目录:
[root@svr5 ~]# awk -F: '/^[a-d]/{print $1,$6}' passwd.txt
bin /bin
daemon /sbin
adm /var/adm
输出其中用户名以a开头、登录Shell以nologin结尾的用户名、登录Shell:
[root@svr5 ~]# awk -F: '/^a|nologin$/{print $1,$7}' passwd.txt
bin /sbin/nologin
daemon /sbin/nologin
adm /sbin/nologin
lp /sbin/nologin
输出其中宿主目录以bin结尾(对第6个字段做~匹配)的用户名、宿主目录信息:
[root@svr5 ~]# awk -F: '$6~/bin$/{print $1,$6}' passwd.txt
bin /bin
daemon /sbin
sync /sbin
shutdown /sbin
输出其中登录Shell不以nologin结尾(对第7个字段做!~反向匹配)的用户名、登录Shell信息:
[root@svr5 ~]# awk -F: '$7!~/nologin$/{print $1,$7}' passwd.txt
root /bin/bash
sync /bin/sync
shutdown /sbin/shutdown
2)使用数值/字符串比较设置条件
输出第3行(行号NR等于3)的用户记录:
[root@svr5 ~]# awk -F: 'NR==3{print}' passwd.txt
daemon:x:2:2:daemon:/sbin:/sbin/nologin
输出奇数行(行号NR除以2余数为1)的用户记录:
[root@svr5 ~]# awk -F: 'NR%2==1{print}' passwd.txt
root:x:0:0:root:/root:/bin/bash
daemon:x:2:2:daemon:/sbin:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
输出偶数行(行号NR除以2余数为0)的用户记录:
[root@svr5 ~]# awk -F: 'NR%2==0{print}' passwd.txt
bin:x:1:1:bin:/bin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
输出前3行文本:
[root@svr5 ~]# awk -F: 'NR<=3{print}' passwd.txt
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
输出从第5行开始到文件末尾的所有行:
[root@svr5 ~]# awk -F: 'NR>=5{print}' passwd.txt
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
输出用户名为“sync”的行:
[root@svr5 ~]# awk -F: '$1=="sync"{print}' passwd.txt
sync:x:5:0:sync:/sbin:/bin/sync
输出当前用户的用户名、宿主目录、登录Shell信息:
[root@svr5 ~]# awk -F: '$1==ENVIRON["USER"]{print $1,$6,$7}' passwd.txt
root /root /bin/bash
3)逻辑测试条件
输出第3~5行文本:
[root@svr5 ~]# awk -F: 'NR>=3&&NR<=5{print}' passwd.txt
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
严谨一点可以写成:
[root@svr5 ~]# awk -F: '(NR>=3)&&(NR<=5){print}' passwd.txt
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
输出第3行和第5行文本:
[root@svr5 ~]# awk -F: 'NR==3||NR==5{print}' passwd.txt
daemon:x:2:2:daemon:/sbin:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
输出“登录Shell不以nologin结尾”或者“用户名以a或d开头”的文本:
[root@svr5 ~]# awk -F: '$7!~/nologin$/||$1~/^[ad]/{print}' passwd.txt
root:x:0:0:root:/root:/bin/bash
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
输出UID小于3或者UID是偶数的用户记录:
[root@svr5 ~]# awk -F: '$3<3||$3%2==0{print}' passwd.txt
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
4)数学运算
以统计passwd.txt文件中以“:”分隔的总字段个数,需要每处理一行时将当前行的字段数(内置变量NF)计和,因此可在BEGIN时定义一个初始变量,过程称求和,最后在END时输出结果。
相关操作及结果如下(共49个字段):
[root@svr5 ~]# awk -F: 'BEGIN{x=0}
{x+=NF} END{print "Total "x" fields."}' passwd.txt
Total 49 fields.
步骤二:完成任务要求的awk过滤操作
1)列出UID间于501~505的用户详细信息:
[root@svr5 ~]# awk -F: '$3>=501&&$3<=505{print}' /etc/passwd
hunter:x:501:501::/home/hunter:/bin/bash
vina:x:502:502::/home/vina:/bin/bash
kdev:x:503:503::/home/kdev:/bin/bash
zengye:x:504:504::/home/zengye:/bin/bash
stu01:x:505:1201::/tech/nsdhome/stu01:/bin/bash
2)输出/etc/hosts映射文件内以127或者192开头的记录:
[root@svr5 ~]# awk -F: '/^127|^192/{print}' /etc/hosts
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
192.168.4.5 svr5.tarena.com svr5
3)列出100以内整数中7的倍数或是含7的数:
此操作无处理文件,正常思路应该是用Shell循环来完成;因为要求用awk来实现,如果不用循环,则根据逐行处理的思路,应该提供一个100行的文本对象,然后将行号作为处理的整数,逐个判断并输出即可。
利用seq命令可生成1-100的整数序列,比如:
[root@svr5 ~]# seq 100
1
2
3
4
5
6
7
8
9
10
.. ..
结合管道交给awk处理,可以简化实现步骤。针对本任务而言,行号与每行的实际文本值是一致的,那么根据NR或者$0行值进行判断都是可以的。输出100以内7的倍数或是包含7的数:
[root@svr5 ~]# seq 100 | awk 'NR%7==0||NR~/7/{print}'
7
14
17
21
27
28
35
37
42
47
.. ..
或者:
[root@svr5 ~]# seq 100 | awk '$0%7==0||$0~/7/{print}'
7
14
17
21
27
28
35
37
42
47
.. ..
3 案例3:awk综合脚本应用
3.1 问题
本案例要求编写脚本getupwd-awk.sh,实现以下需求:
找到使用bash作登录Shell的本地用户
列出这些用户的shadow密码记录
按每行“用户名 --> 密码记录”保存到getupwd.log,如图-2所示
图-2
3.2 步骤
实现此案例需要按照如下步骤进行。
步骤一:任务需求及思路分析
编写getupwd-awk.sh脚本的任务要求如下:
分析出使用bash作登录Shell的本地用户
列出这些用户的shadow密码记录
按每行“用户名 -- 密码记录”保存结果
步骤二:根据实现思路编写脚本
复制原getupwd.sh脚本,生成getupwd-awk.sh:
[root@svr5 ~]# cat getupwd.sh //确认原脚本内容
#/bin/bash
> /tmp/getupwd.log ## 创建空文件
sed -n '/:/bin/bash$/w /tmp/urec.tmp' /etc/passwd ## 提取符合条件的账号记录
UNUM=$(egrep -c '.' /tmp/urec.tmp) ## 取得记录个数
while [ ${i:=1} -le $UNUM ] ## 从第1行开始,遍历账号记录
do
UREC=$(sed -n "${i}p" /tmp/urec.tmp) ## 取指定行数的记录
NAME=${UREC%%:*} ## 截取用户名(记录去尾)
PREC=$(sed -n "/^$NAME:/p" /etc/shadow) ## 查找与用户名对应的密码记录
PASS=${PREC#*:} ## 掐头
PASS=${PASS%%:*} ## 去尾,只留下密码记录
echo "$NAME --> $PASS" >> /tmp/getupwd.log ## 保存结果
let i++ ## 自增1,转下一次循环
done
/bin/rm -rf /tmp/urec.tmp ## 删除临时文件
echo "用户分析完毕,请查阅文件 /tmp/getupwd.log" ## 完成后提示
[root@svr5 ~]# cp getupwd.sh getupwd-awk.sh //复制为新脚本文件
修改新脚本文件,内容参考如下:
[root@svr5 ~]# vim getupwd-awk.sh
#/bin/bash
## 创建空文件
> /tmp/getupwd.log
## 提取用户名列表
awk -F: '/:/bin/bash$/{print $1}' /etc/passwd > /tmp/users.tmp
## 通过for循环遍历用户名、查询密码记录,保存结果
for NAME in $(cat /tmp/users.tmp)
do
grep "^$NAME:" /etc/shadow | awk -F: '{print $1" --> "$2 |
"cat >> /tmp/getupwd.log"}' /etc/shadow
done
echo "用户分析完毕,请查阅文件 /tmp/getupwd.log" ## 完成后提示
[root@svr5 ~]# chmod +x getupwd-awk.sh
步骤三:验证、测试脚本
[root@svr5 ~]# ./getupwd-awk.sh
用户分析完毕,请查阅文件 /tmp/getupwd.log
[root@svr5 ~]# head -5 /tmp/getupwd.log
root --> $6$IWgMYmRACwdbfwBo$dr8Yn983nswiJVw0dTMjzbDvSLeCd1GMYjbvsDiFEkL8jnXOLcocBQypOCr4C6BRxNowIxjh6U2qeFU0u1LST/
zengye --> $6$Qb37LOdzRl5995PI$L0zTOgnhGz8ihWkW81J.5XhPp/l7x2./Me2ag0S8tRndCBL9nIjHIKkUKulHxJ6TXyHYmffbVgUT6pbSwf8O71
clamav --> !!
mysql --> !!
abc --> !!
.. ..
4 案例4:awk流程控制
4.1 问题
本案例要求了解awk的流程控制操作,可自行设置awk语句来有验证以下操作:
if分支结构(双分支、多分支)
while循环结构
break、continue等其他控制语句
4.2 步骤
实现此案例需要按照如下步骤进行。
步骤一:awk过滤中的if分支结构
1)单分支
统计/etc/passwd文件中UID小于或等于500的用户个数:
[root@svr5 ~]# awk -F: 'BEGIN{i=0}{if($3<=500){i++}}END{print i}' /etc/passwd
39
统计/etc/passwd文件中UID大于500的用户个数:
[root@svr5 ~]# awk -F: 'BEGIN{i=0}{if($3>500){i++}}END{print i}' /etc/passwd
28
统计/etc/passwd文件中登录Shell是“/bin/bash”的用户个数:
[root@svr5 ~]# awk -F: 'BEGIN{i=0}{if($7~/bash$/){i++}}END{print i}'
/etc/passwd
29
统计/etc/passwd文件中登录Shell不是“/bin/bash”的用户个数:
[root@svr5 ~]# awk -F: 'BEGIN{i=0}{if($7!~/bash$/){i++}}END{print i}'
/etc/passwd
38
2)双分支
分别统计/etc/passwd文件中UID小于或等于500、UID大于500的用户个数:
[root@svr5 ~]# awk -F: 'BEGIN{i=0;j=0}{if($3<=500){i++}else{j++}}END{print i,j}' /etc/passwd
39 28
分别统计/etc/passwd文件中登录Shell是“/bin/bash”、 登录Shell不是“/bin/bash”的用户个数:
[root@svr5 ~]# awk -F: 'BEGIN{i=0;j=0}{if($7~/bash$/){i++}else{j++}}
END{print i,j}' /etc/passwd
29 38
3)多分支
分别统计/etc/passwd文件中登录Shell是“/bin/bash”、“/sbin/nologin”、其他的用户个数:
[root@svr5 ~]# awk -F: 'BEGIN{i=0;j=0;k=0}{if($7~/bash$/){i++}
else if($7~/nologin$/){j++}else{k++}}END{print i,j,k}' /etc/passwd
29 33 5
步骤二:awk过滤中的while循环结构
1)while循环
统计/etc/passwd文件内“root”出现的次数。
—— 分析:以“:”或“/”做分隔,针对每一行的每一列进行比对,如果包含“root”,则次数加1。其中,逐行处理直接由awk完成,逐列处理交给while循环,通过i变量依次取$1、$2、……、$NF进行检查;变量j在预处理时赋值0,没匹配一个字段加1。
[root@svr5 ~]# awk -F [:/]
'BEGIN{j=0}
{i=1}{while(i<=NF){if($i~/root/){j++};i++}}
END{print j}' /etc/passwd
4
此例仅为说明while循环的用法。
实际应用时,上述操作可以简单处理,可通过命令替换将文件内容赋值给一个变量(变为一行文本),然后针对此变量值以目标字符串“root”作为分隔,获取总字段数-1即可得目标字符串的总数量:
[root@svr5 ~]# echo $(cat /etc/passwd) | awk -F "root" '{print NF-1}'
4
5 案例5:awk扩展应用
5.1 问题
本案例要求使用awk工具完成下列两个任务:
分析Web日志的访问量排名,要求获得客户机的地址、访问次数,并且按照访问次数排名
5.2 方案
1)awk统计Web访问排名
在分析Web日志文件时,每条访问记录的第一列就是客户机的IP地址,其中会有很多重复的IP地址。因此只用awk提取出这一列是不够的,还需要统计重复记录的数量并且进行排序。
通过awk提取信息时,利用IP地址作为数组下标,每遇到一个重复值就将此数组元素递增1,最终就获得了这个IP地址出现的次数。
针对文本排序输出可以采用sort命令,相关的常见选项为-r、-n、-k。其中-n表示按数字顺序升序排列,而-r表示反序,-k可以指定按第几个字段来排序。
5.3 步骤
实现此案例需要按照如下步骤进行。
步骤一:统计Web访问量排名
分步测试、验证效果如下所述。
1)提取IP地址及访问量
[root@svr5 ~]# awk '{ip[$1]++} END{for(i in ip) {print i,ip[i]}}' /var/log/httpd/access_log
127.0.0.1 4
192.168.4.5 17
192.168.4.110 13
.. ..
2)对第1)步的结果根据访问量排名
[root@svr5 ~]# awk '{ip[$1]++} END{for(i in ip) {print i,ip[i]}}' /var/log/httpd/access_log | sort -nr -k 2
192.168.4.5 17
192.168.4.110 13
127.0.0.1 4
.. ..