八、shell三剑客3—awk
8.1 awk简介
awk 是一种编程语言,用于在linux/unix下对文本和数据进行处理。数据可以来自标准输入、一个或多个文件,或其它命令的输出。可以在命令行中使用,但更多是作为脚本来使用。
awk的处理文本和数据的方式是这样的,它逐行扫描文件,从第一行到最后一行,寻找匹配的特定模式的行,并在这些行上进行操作。如果没有指定处理动作,则把匹配的行显示到标准输出(屏幕),如果没有指定模式,则所有被操作所指定的行都被处理。
awk分别代表其作者姓氏的第一个字母。因为它的作者是三个人,分别是Alfred Aho、Brian Kernighan、Peter Weinberger。
-
awk是行处理器:优势在于处理庞大文件时不会出现内存溢出或是处理缓慢的问题;
-
awk处理过程:依次对每一行进行处理,然后输出,默认分隔符是空格或者tab键。
8.2 awk语法
awk [options] 'commands' filenames
options:
-F 对于每次处理的内容,可以指定一个自定义的分隔符,默认的分隔符是空白字符(空格或 tab 键 )
8.3 awk工作原理
awk -F":" '{print $1,$3}' /etc/passwd
(1)awk使用一行作为输入,并将这一行赋给变量$0,每一行可称作为一个记录,以换行符结束
(2)然后,行被空格分解成字段,每个字段存储在已编号的变量中,从$1开始
(3)awk如何知道空格来分隔字段的呢?因为有一个内部变量FS来确定字段分隔符,初始时,FS赋为空格或者是tab
(4)awk打印字段时,将以设置的方法,使用print函数打印,awk在打印的字段间加上空格,因为$1,$2间有一个,逗号。逗号比较特殊,映射为另一个变量,成为输出字段分隔符OFS,OFS默认为空格
(5)awk打印字段时,将从文件中获取另一行,并将其存储在$0中,覆盖原来的内容,然后将新的字符串分隔成字段并进行处理。该过程持续到处理文件结束。
command:
语法:awk ‘BEGIN{} {} END{} ’ 文件名
BEGIN{}和END{}可选
BEGIN{} {} END{} filename
行处理前的动作 行内容处理的动作 行处理之后的动作 文件名
BEGIN{}和END{} 是可选项。
函数-BEGIN{}:读入文本之前要操作的命令。(也可以设置变量.取值可以不加$)
{}:主输入循环:用的最多。读入文件之后擦操作的命令。如果不读入文件都可以不用写。
END{}:文本全部读入完成之后执行的命令。
示例
[root@awk ~]# awk 'BEGIN{ print 1/2} {print "ok"} END{print "----"}' /etc/hosts
或者:
[root@awk ~]# cat /etc/hosts | awk 'BEGIN{print 1/2} {print "ok"} END{print "----"}'
0.5
ok # 有两行ok内容,表明hosts文件中有两行记录
ok
----
awk 'BEGIN{print ""} {print ""} END{print ""}'
8.4 记录与字段相关内部变量
1.记录和字段
awk 按记录处理:'一行是一条记录',因为awk默认以换行符分开的字符串是一条记录。(默认\n换行符:记录分隔符)
字段:以字段分割符分割的字符串(默认是单个或多个“ ” tab键来分割)
2.awk中的变量
$0: # 表示整行;
$1: # 第一个字段
$2: # 第二个字段,依次类推...
NF: # 统计字段的个数
$NF: # 是number finally,表示最后一列的信息
RS: # 输入记录分隔符;
ORS: # 输出记录分隔符。
NR: # 打印记录号,(行号)
FNR: # 可以分开,按不同的文件打印行号。
FS: # 输入字段分隔符(默认为一个空格)--简写为“-F”,即后面跟什么就说明是以什么作为分隔符。
OFS: # 输出的字段分隔符(默认为一个空格)--比如将分隔符 : 以 .. 输出。
FILENAME # 文件名 被处理的文件名称
实战
【FS】:输入字段分隔符---一般简写为-F'(属于行处理前)'
[root@awk ~]# cat /etc/passwd | awk 'BEGIN{FS=":"} {print $1,$2}'
root x
bin x
daemon x
adm x
lp x
sync x
shutdown x
halt x
mail x
[root@awk ~]# cat /etc/passwd | awk -F":" '{print $1,$2}'
root x
bin x
daemon x
adm x
lp x
sync x
shutdown x
halt x
mail x
#注:如果-F不加默认以空格区分!
【OFS】:(输出字段分隔符)
[root@awk ~]# cat /etc/passwd | awk 'BEGIN{FS=":";OFS=".."} {print $1,$2}'
root..x
bin..x
daemon..x
adm..x
lp..x
sync..x
shutdown..x
halt..x
mail..x
【NR】:表示记录编号, '在awk将行做为记录,' 该变量相当于当前行号,也就是记录号'(属于行处理时或行处理前)'
创建两个文件
[root@awk ~]# vim a.txt # 文件1
love
love.
loove
looooove
[root@awk ~]# vim file1.txt # 文件2
isuo
IPADDR=192.168.246.211
hjahj123
GATEWAY=192.168.246.1
NETMASK=255.255.255.0
DNS=114.114.114.114
[root@awk ~]# awk '{print NR,$0}' a.txt file1.txt # NR测试
1 love
2 love.
3 loove
4 looooove
5
6 isuo
7 IPADDR=192.168.246.211
8 hjahj123
9 GATEWAY=192.168.246.1
10 NETMASK=255.255.255.0
11 DNS=114.114.114.114
【FNR】:表示记录编号, 在awk将行做为记录, 该变量相当于当前行号,也就是记录号('会将不同文件分开')'(属于行处理时)'
[root@awk ~]# awk '{print FNR,$0}' a.txt file1.txt # FNR测试
1 love
2 love.
3 loove
4 looooove
5
1 isuo
2 IPADDR=192.168.246.211
3 hjahj123
4 GATEWAY=192.168.246.1
5 NETMASK=255.255.255.0
6 DNS=114.114.114.114
【RS】:输入记录分隔符,即后面跟什么,就表示以什么作为换行符(同时会删除该换行符)'(属于行处理前)'
创建一个文件
[root@awk ~]# vim passwd
root:x:0:0:root:/root:/bin/bashbin:x:1:1:bin:/bin:/sbin/nologin
[root@awk ~]# cat passwd | awk 'BEGIN{RS="bash"} {print $0}'
root:x:0:0:root:/root:/bin/
bin:x:1:1:bin:/bin:/sbin/nologin
【ORS】:输出记录分隔符'(属于行处理前)'
对刚才的文件进行修改
[root@awk ~]# vim passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
[root@awk ~]# cat passwd | awk 'BEGIN{ORS=" "} {print $0}'
root:x:0:0:root:/root:/bin/bash bin:x:1:1:bin:/bin:/sbin/nologin
【NF】:统计列的个数'(属于行处理前)'
[root@awk ~]# cat /etc/passwd | awk -F":" '{print NF}'
7 # 表明第1行以冒号(:)分割的列数为7
7
7
7
【$NF】:打印最后一列'(属于行处理前)'
[root@awk ~]# cat /etc/passwd | awk -F":" '{print $NF}'
/bin/bash
/sbin/nologin
/sbin/nologin
/sbin/nologin
/sbin/nologin
8.5 关系运算符号
实现 字符串的完全相等需要使用 ==
字符串需要使用双引号
!=表示不等于
[root@awk ~]# awk -F":" '$NF == "/bin/bash"' /etc/passwd 打印最后一列为/bin/bash的所有记录
[root@awk ~]# awk -F":" '$1 != "root"' /etc/passwd 打印第一列不是root的所有记录
- 比较表达式:
比较表达式采用对文本进行比较,只有当条件为真,才执行指定的动作。
比较表达式使用关系运算符,用于比较数字与字符串。关系运算符有
<小于 例如x<y
>大于x>y
<=小于或等于x<=y
==等于x==y
!=不等于x!=y
>=大于等于x>=y
示例
[root@awk ~]# awk -F":" '$3 == 0' /etc/passwd # 像这种没有写花括号的,默认在是处理时
[root@awk ~]# awk -F":" '$3 < 10' /etc/passwd
- 算术运算:
+,-,*,/,%(模: 取余),^(幂:2^3)\
可以在模式中执行计算,awk都将按浮点数方式执行算术运算
awk -F: '$3 * 10 > 500' /etc/passwd
- 逻辑操作符和复合模式
&&逻辑与, 相当于 并且
||逻辑或,相当于 或者
!逻辑非 , 取反
awk -F":" '$1~/root/ && $3<=15' /etc/passwd # 匹配有root关键字的行且第3列值小于的所有行
awk -F":" '$1~/root/ || $3<=15' /etc/passwd # 匹配有root关键字的行或第3列值等于的所有行
8.6 常见使用
1.打印一个文件中的第2列和第5列
# cat /etc/passwd | awk -F : '{print $2,$5}'
2.打印指定行指定列的某个字符
# free -m | awk 'NR==2 {print $2}'
3.统计一个文件的行数
# cat /etc/passwd | awk '{print NR}'
4.在awk中使用if条件判断
++i:从1开始加,赋值在运算
i++: 从0开始加,运算在赋值
if语句:
{if(表达式){语句;语句;...}}
实战案例:
显示管理员用户姓名
[root@qfedu ~]# cat /etc/passwd | awk -F":" '{if($3==0) {print $1 " is administrator"}}'
统计系统用户数量
[root@qfedu ~]# cat /etc/passwd | awk -F":" '{if($3>=0 && $3<=1000){i++}} END{print i}'
5.在awk中使用for循环
每行打印两遍
[root@qfedu ~]# awk '{for(i=1;i<=2;i++) {print $0}}' /etc/passwd
root:x:0:0:root:/root:/bin/bash
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
数组遍历--用来统计网站日志,tcp连接状态等。
++i:从1开始加,赋值在运算
i++: 从0开始加,运算在赋值
#按索引遍历:
1.先创建一个test文件
# vim test.txt #将文件内容的第一个字段作为数组的值,通过索引获取到值
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
# cat test.txt | awk -F":" '{username[x++]=$1} END{for(i in username) {print i,username[i]}}'
0 root
1 bin
#注意:变量i是索引
8.7 案例
#把要统计的对象作为索引,最后对他们的值进行累加,累加出来的这个值就是你的统计数量
1. 统计/etc/passwd中各种类型shell的数量
# cat /etc/passwd | awk -F: '{shells[$NF]++} END{ for(i in shells){print i,shells[i]} }'
2.统计nginx日志出现的状态码
# cat access.log | awk '{stat[$9]++} END{for(i in stat){print i,stat[i]}}'
3.统计当前nginx日志中每个ip访问的数量
# cat access.log | awk '{ips[$1]++} END{for(i in ips){print i,ips[i]}}'
4.统计某一天的nginx日志中的不同ip的访问量
# cat access.log |grep '28/Sep/2019' | awk '{ips[$1]++} END{for(i in ips){print i,ips[i]}}'
5.统计nginx日志中某一天访问最多的前10个ip
# cat access.log |grep '28/Sep/2019' | awk '{ips[$1]++} END{for(i in ips){print i,ips[i]}}' |sort -k2 -rn | head -n 2
sort:排序,默认升序
-k:指定列数
-r:降序
-n:以数值来排序
6.统计tcp连接的状态---下去自己查各个状态,包括什么原因造成的!
# netstat -n | awk '/^tcp/ {tcps[$NF]++} END {for(i in tcps) {print i, tcps[i]}}'
LAST_ACK 5 (正在等待处理的请求数)
SYN_RECV 30
ESTABLISHED 1597 (正常数据传输状态)
FIN_WAIT1 51
FIN_WAIT2 504
TIME_WAIT 1057 (处理完毕,等待超时结束的请求数)
九、Expect
9.1 expect的安装
[root@qfedu ~] yum -y install expect
9.2 expect的语法
是一个免费的编程工具, 用来实现自动的交互式任务, 而无需人为干预. 说白了 expect 就是一套用来实现自动交互功能的软件
在实际工作中我们运行命令、脚本或程序时, 都需要从终端输入某些继续运行的指令,而这些输 入都需要人为的手工进行. 而利用 expect 则可以根据程序的提示, 模拟标准输入提供给程序, 从而实现自动化交互执 行. 这就是 expect
如果能够在工作中熟练的使用Shell脚本就可以很大程度的提高工作效率, 那么再搭配上expect这个时候很多工作都可以实现自动化进行。
用法:
1)定义expect脚本执行的shell
#!/usr/bin/expect -----类似于#!/bin/bash
2)set timeout 30
设置超时时间30s
3)spawn
spawn是执行expect之后后执行的内部命令开启一个会话 #功能:用来执行shell的交互命令
4)expect ---相当于捕捉
功能:判断输出结果是否包含某项字符串(相当于捕捉返回的结果),没有则会断开,否则等待一段时间后返回,等待通过timeout设置
5)send
执行交互动作,将交互要执行的命令进行发送给交互指令,命令字符串结尾要加上“\r”,#---相当于回车
6)interract
执行完后保持交互状态,需要等待手动退出交互状态,如果不加这一项,交互完成会自动退出
7)exp_continue
继续执行接下来的操作
9.3 实战非交互式ssh连接
案例1:普通操作
[root@qfedu script]# vim expect01.sh
#!/usr/bin/expect
spawn ssh root@192.168.246.115
expect {
"yes/no" { send "yes\r"; exp_continue }
"password:" { send "1\r" };
}
interact
[root@qfedu script]# chmod +x expect01.sh
[root@qfedu script]# ./expect01.sh
spawn ssh root@192.168.246.115
root@192.168.246.115's password:
Last login: Fri Aug 28 16:57:09 2019
#如果添加interact参数将会等待我们手动交互进行退出。如果不加interact参数在登录成功之后会立刻退出。
============================================================================
2.设置变量与进行传参的方式
#注意:expect环境中设置变量用set,识别不了bash方式定义的变量
[root@qfedu script]# vim expect01.sh
#!/usr/bin/expect
set user root
set pass 1
set ip [ lindex $argv 0 ] #expect固定写法,从0开始,第一个位置参数,相当于shell中的$1
set timeout 10
spawn ssh $user@$ip
expect {
"yes/no" { send "yes\r"; exp_continue }
"password:" { send "$pass\r" };
}
interact
[root@qfedu script]# ./expect01.sh 192.168.246.115
spawn ssh root@192.168.246.115
root@192.168.246.115's password:
Last login: Fri Aug 28 07:13:57 2019 from 192.168.246.135
#如果想登录成功自动结束交互模式也就是expect,可以采用下面方式:
expect "#"
send "useradd test\r"
send "pwd\r"
send "exit\r"
expect eof #直接退出expect模式
============================================================================
3.进行批量推送公钥实现免密连接,ping通一个ip地址连接一个ip------!!!!扩展
[root@qfedu script]# vim getip_push.sh
#!/usr/bin/bash
pass=1
#判断expect命令是否安装
rpm -qa expect &> /dev/null
if [ $? -ne 0 ];then
yum install -y expect
fi
#判断主机下面是否生成秘钥,如果没有生成秘钥
if [ ! -f ~/.ssh/id_rsa ];then
ssh-keygen -P "" -f ~/.ssh/id_rsa
fi
#循环执行获取up状态的ip地址。
for i in {2..254}
do
{
ip=192.168.198.$i
ping -c1 $ip &> /dev/null
if [ $? -eq 0 ];then
echo "$ip" >> up_ip.txt
set timeout 10
/usr/bin/expect <<-EOF #shell脚本中调用expect命令
spawn ssh-copy-id $ip
expect {
"yes/no" { send "yes\r"; exp_continue }
"password:" { send "$pass\r" };
}
expect eof
EOF
fi
} &
wait
done
echo "finish..."
[root@qfedu script]# chmod +x getip_push.sh
[root@qfedu script]# ./getip_push.sh
测试....
9.4 运维脚本实战
• 实战项目1: 实现网络配置
• 实战项目2: sed实现sshd配置
• 实战项目3: sed实现nginx配置修改端口为8080
• 实战项目4: sed实现关闭本机SELinux的功能(/etc/sysconfig/selinux)
• 实战项目5: awk统计/etc/passwd各种shell数量
• 实战项目6: awk统计网站访问各种状态数量
• 实战项目7: awk统计访问的每个IP的数量
• 实战项目8: 统计Nginx日志中某一天的PV量
• 实战项目9: 获取获得内存使用情况
• 实战项目10: 基于时间的备份脚本
• 实战项目11: Web日志访问量分析程序(PV、UV)
• 实战项目12: 编写系统初始化脚本
1)设置时区并把同步时间加入计划任务
2)禁用selinux
3)历史命令显示操作时间
4)创建ALL权限用户并禁止root远程登录
5)安装常用工具
• 实战项目13: LAMP终级部署(rpm包方式)
• 实战项目14: Linux系统状态收集及分析(内存使用量,硬盘使用量,cpu使用量等)
项目作为作业

&spm=1001.2101.3001.5002&articleId=147770883&d=1&t=3&u=0bac82e0b70746d18881114caacaf32b)

被折叠的 条评论
为什么被折叠?



