一、正则表达式
正则表达式用于查找匹配指定的字符
支持正则表达式的程序:locate、find、vim、grep、sed、awk
元字符:具有特殊意义的专用字符,如. * ?
前导字符:位于元字符前面的字符,如abc*
1、第一类正则表达式
普通常用的元字符
. 匹配除换行符以外的任意单个字符
* 前导字符出现0次或连续多次
.* 任意长度字符
^ 行首
$ 行尾
^$ 空行
[] 匹配括号里任意单个字符或一组单个字符
[^] 匹配不包含括号里任意单个字符或一组单个字符
^[] 匹配以括号里任意单个字符或一组单个字符开头
^[^] 匹配不以括号里任意单个字符或一组单个字符开头
其他常用元字符
\< 匹配单词的开头
\> 匹配单词的结尾
\< \> 精确匹配单词
\{n\} 匹配前导字符连续出现n次
\{n,\} 匹配前导字符至少出现n次
\{n,m\} 匹配前导字符出现n~m次
\( \) 保存被匹配的字符
\d 匹配数字(grep -P) [0-9]
\w 匹配字母数字下划线(grep -P) [a-zA-Z0-9_]
\s 匹配空格、制表符、换页符(grep -P) [\t\r\n]
扩展类正则常用元字符
grep -E 或 egrep
sed -r
+ 匹配一个或多个前导字符
? 匹配0个或一个前导字符
| 或
() 组字符(看成整体)
{n} 前导字符重复n次
{n,} 前导字符重复至少n次
{n,m} 前导字符重复n~m次
2、第二类正则表达式
[:alnum:] 字母、数字字符 [[:alnum:]]+
[:alpha:] 字母字符(大小写) [[:alpha:]]{4}
[:blank:] 空格与制表符 [[:blank:]]*
[:digit:] 数字 [[:dight:]]?
[:lower:] 小写字母 [[:lower:]]{4,}
[:upper:] 大写字母 [[:upper:]]+
[:punct:] 标点符号 [[:punct:]]
[:space:] 换行符、回车等所有空白 [[:space:]]+
二、sed
1、命令行格式
sed [options] '处理动作' 文件名
-e 进行多项编辑
-n 取消默认输出
-r 使用扩展正则表达式
-i 原地编辑(修改源文件)
-f 指定sed脚本文件名
常见处理动作
'p' 打印
'i' 在指定行之前插入内容
'a' 在指定行之后插入内容
'c' 替换指定行所有内容
'd' 删除指定行
对文件进行增删改查操作
语法:sed 选项 ‘定位+命令’ 需要处理的文件
打印文件内容
sed '' a.txt
sed -n '2p' a.txt
sed -n '1,5p' a.txt
sed -n '$p' a.txt
增加文件内容
sed '2ihello world' a.txt
sed '3ihello\nworld' a.txt
sed '2,4a999' a.txt
修改文件内容
sed '$chello world' a.txt
sed '/^adm/chello world' a.txt
sed '1,5chello world' a.txt 将1~5行替换为一行hello world
删除文件内容
sed '1,4d' a.txt
sed -r '/([0-9]{1,3}\.){3}[0-9]{1,3}/d' a.txt
对文件进行搜索替换操作
语法:sed 选项 ‘s/搜索的内容/替换的内容/动作’ 需要处理的文件
s表示搜索;/表示分隔符,可自定义;动作有打印p和全局替换g
sed -n 's/root/ROOT/p' 1.txt
sed -n 's/root/ROOT/gp' 1.txt
sed -n 's/^#//gp' 1.txt 去掉#号
sed -n 's@/sbin/nologin@hello@gp' 1.txt
sed -n '10s@/sbin/nologin@hello@gp' 1.txt 替换第10行
sed -n '1,5s/^/#/gp' 1.txt
sed -n 's#\(10.1.1.\)1#\1254#gp' 1.txt
其他命令
r 从另外文件中读取内容
w 内容另存为
& 与\(\)相同
= 打印行号
! 对所选行以外的其他行应用命令,放到行数之后
q 退出
sed '3r /etc/hosts' 1.txt 在第三行读取其他文件内容
sed '1,5w 11.txt' 1.txt 将前五行另存为其他文件
sed -n 's/^sync/#&/gp' 1.txt 在sync行添加注释
sed -ne '/root/p' -ne '/root/=' 1.txt 先打印行,后面打印行号
sed -n '/root/=;/root/p' 1.txt
sed -n '1,5!p' 1.txt 不包括1~5行
sed '/shutdown/q' 1.txt 读取到shutdown行退出
其他选项
-e 多项编辑
-r 扩展正则
-i 修改原文件
grep -v '^#' vsftpd.conf |grep -v '^$' 过滤配置文件中注释行和空行
sed -e '/^#/d' -e '/^$/d' vsftpd.conf
sed -e '/^#/d;/^$/d' vsftpd.conf
sed -r '/^#|^$/d' vsftpd.conf
sed -i 's/root/ROOT/g' 1.txt
sed结合正则使用
sed 选项 ‘sed命令或者正则表达式或者地址定位’ 文件名
/key/ sed -n '/root/p' 1.txt 查询包含关键字的行
/key1/,/key2/ sed -n '/^adm/,/^mysql/p' 1.txt 匹配包含两个关键字之间的行
/key/,x sed -n '/^ftp/,7p' 1.txt 匹配包含关键字的行到第x行
x,/key/
x,y! 不包含x到y行
/key/! sed -n '/bash$/!p' 1.txt 不包含关键字的行
2、脚本格式
sed -f scripts.sh file
./sed.sh file
#!/bin/sed -f
1,5d
s/root/ROOT/g
3i777
5i888
a999
p
注意:脚本文件是一个sed的命令行清单;
在每行末尾不能有空格、制表符等其他文本
一行有多个命令用分号分隔
不可用引号
#号开头的行为注释
三、awk
1、语法结构:
awk 选项 '命令部分' 文件名 (引用shell变量需用双引号)
-F 定义字段分隔符,默认分隔符为空格
-v 定义变量并赋值
命令部分说明:
正则表达式,地址定位
'/root/{awk语句}' sed中'/root/p'
'NR==1,NR==5{awk语句}' '1,5p'
'/^root/,/^ftp/{awk语句}' '/^root/,/^ftp/p'
{awk语句1;awk语句2;...}
'{print $0;print $1}' 'p'
'NR==5{print $0}' '5p'
#awk语句间用分号间隔
BEGIN...END...
'BEGIN{awk语句};{处理中};END{awk语句}'
'BEGIN{awk语句};{处理中}'
'{处理中};END{awk语句}'
2、脚本模式
#!/bin/awk -f
#以下awk引号内的命令清单不要用引号,多个命令使用分号隔开
BEGIN{FS=":"}
NR==1,NR==3{print $1"\t"$NF}
...
脚本执行
方法一:
awk 选项 -f awk脚本文件 需处理的文件
awk -f awk.sh filename
awk -f awk.sh -i filename
方法二:
./awk脚本文件(或绝对路径) 需处理的文件
./awk.sh filename
3、awk内部相关变量
$0 当前处理行的所有记录
$1,$2,$3...$n 每行以间隔符号分隔的不同字段 awk -F:'{print $1,$3}'
NF 当前记录的字段数(列数) awk -F: '{print NF}'
$NF 最后一列 $(NF-1)表示倒数第二列
FNR/NR 行号
FS 定义间隔符 'BEGIN{FS=":"};{print $1,$3}'
OFS 定义输出字段分隔符,默认空格 'BEGIN{OFS="\t"};{print $1,$3}'
RS 输入记录分隔符,默认换行 'BEGIN{RS="\t"};{print $0}'
ORS 输出记录分隔符,默认换行 'BEGIN{PRS="\n\n"};{print $1,$3}'
FILENAME 文件名
4、awk使用进阶
格式化输出print和printf
print函数 相当于echo
date |awk '{print "Month: "$2 "\nYear: "$NF}'
printf函数 相当于echo -n
awk -F: '{printf "%-15s %-10s %-15s\n", $1,$2,$3}' /etc/passwd
awk -F: '{printf "|%-15s| %-10s| %-15s|\n", $1,$2,$3}' /etc/passwd
awk变量定义
awk -v NUM=3 -F: '{print NUM}' 1.txt
awk -v NUM=3 'BEGIN{print NUM}'
#awk中调用定义的变量不需要加$
awk中的BEGIN...END
BEGIN表示程序开始前执行
END表示所有文件处理完后执行
'BEGIN{开始处理前};{处理中}:END{处理结束后}'
例:打印最后一列和倒数第二列
awk -F: 'BEGIN{print "Login_shell\t\tLogin_home\n*************"};{print $NF"\t\t"$(NF-1)};END{print "*************"}' 1.txt
2、打印/etc/passwd里的用户名、家目录、登录shell
awk -F: 'BEGIN{OFS="\t\t";print "u_name\t\th_dir\t\tshell\n****************"};{printf "%-20s %-20s %-20s\n",$1,$(NF-1),$NF};END{print "******************"}' /etc/passwd
awk与正则运用
== 等于
!= 不等于
> 大于
< 小于
>= 大于等于
<= 小于等于
~ 匹配
!~ 不匹配
! 逻辑非
&& 逻辑与
|| 逻辑或
awk -F: 'NR==1,/^lp/{print $0}' passwd
awk -F: '/^root/ || /^lp/{print $0}' passwd
awk 'NR>=30 && NR<=39 && $0 ~ /bash$/{print $0}' passwd
打印ip地址
ifconfig eth0 |grep Bcast|awk -F: '{print $2}' |awk '{print $1}'
ifconfig eth0 |grep Bcast|awk -F'[: ]+' '{print $4}'
ifconfig eth0 |awk -F"[: ]+" '/inet addr/{print $4}'
5、awk脚本编程
流程控制语句
if结构
格式:awk 选项 '正则,地址定位{awk语句}' 文件名
{if(表达式) (语句1;语句2;...)}
awk -F: '{if($3==0) {print $1"是管理员"}}' passwd
awk 'BEGIN{if($(id -u)==0) {print "admin"}}'
if...else结构
格式:{if(表达式) {语句;语句;...} else {语句;语句;...}}
awk -F: '{if($3>=500 && $3 !=65534) {print $1"是普通用户"} else {print $1"不是普通用户"}}' passwd
awk 'BEGIN{if($(id -u)>=500 && $(id -u) !=65534) {print "是普通用户"} else {print "不是普通用户"}}'
if...else if...else结构
格式:{if(表达式1) {语句;语句;...} else if(表达式2) {语句;语句;...} else if(表达式3) {语句;语句;...} else {语句;语句;...}}
awk -F: '{if($3==0) {print $1"is admin"} else if($3>=500 && $3<=60000) {print $1"is normal user"} else {print $1"is system user"}}' passwd
awk -F: '{if($3==0) {i++} else if($3>=1 && $3<=499 || $3==65534) {j++} else {k++}};END{print "管理员个数为:"i"\n系统用户个数为:"j"\n普通用户个数为:"k}' passwd
循环语句
for循环
awk 'BEGIN{for(i=1;i<=5;i++) {print i}}'
awk 'BEGIN{sum=0;for(i=1;i<=10;i+=2) sum=sum+i;{print sum}}'
for ((i=1;i<=10;i+=2));do echo $i;done|awk '{sum+=$0};END{print sum}'
while循环
awk 'BEGIN{i=1;while(i<=5) {print i;i++}}'
awk 'BEGIN{sum=0;i=1;while(i<=10) {sum=sum+i;i+=2};{print sum}}'
嵌套循环
awk 'BEGIN{for(y=1;y<=5;y++) {for(x=1;x<=y;x++) {printf x};print}}'
1
12
123
1234
12345
99乘法口诀
awk 'BEGIN{for(y=1;y<=9;y++) {for(x=1;x<=y;x++) {k=y*x;printf (x"*"y"="k"\t")};print ("\n")}}'
awk算数运算
+ - * / %(模) ^(幂2^3)
awk 'BEGIN{print 1+1}'
awk 'BEGIN{print 2**3}'
awk 'BEGIN{print 2/3}'
6、awk练习——统计
统计系统中的shell
awk -F: '{shells[$NF]++};END{for (i in shells) {print i,shells[i]}}' /etc/passwd
统计网站访问状态
ss -antp|grep 80|awk '{states[$1]++};END{for (i in states) {print i,states[i]}}'
ss -an|grep :80|awk '{states[$2]++};END{for (i in states) {print i,states[i]}}'|sort -k2 -rn
统计访问网站的每个IP数量
netstat -ant|grep :80|awk -F: '{ip_count[$8]++};END{for (i in ip_count) {print i,ip_count[i]}}'|sort
ss -an|grep :80|awk -F: '!/LISTEN/{ip_count[$(NF-1)]++};END{for (i in ip_count) {print i,ip_count[i]}}'|sort -k2 -rn|head
统计网站日志PV量
统计Apache/Nginx日志中的一天PV量
grep '24/May/2020' mysqladmin.cc-access_log |wc -l
统计Apache/Nginx日志中的一天不同IP访问量
grep '24/May/2020' mysqladmin.cc-access_log|awk '{ips[$1]++};END{for (i in ips) {print i,ips[i]}}'|sort -k2 -rn|head
grep '24/May/2020' access_log|awk '{ips[$1]++};END{for (i in ips) {print i,ips[i]}}'|awk '$2>100'|sort -k2 -rn
*****专业术语解释*****
网站浏览量(PV)指页面浏览次数,用户每打开一个页面记录1次PV;
访问次数(VV)从访客进入网站到最后关闭所有页面离开为1次访问,连续30分钟没有新开或刷新网页,或关闭浏览器,为本次访问结束;
独立访客(UV)1天内相同访客多次访问网站为1个UV;
独立IP(IP)1天内使用不同IP地址的用户访问网站的数量,同一IP访问多个网页,独立IP均为1。
7、实战练习(日志转储)
要求:需要将每台web服务器上的apache访问日志保留最近3天,3天以前的日志转储到专门的日志服务器,如何实现每台服务器上只保留3天以内的日志?
1、每台web服务器日志对应日志服务器相应的目录;
2、每台web服务器保留3天的访问日志,3天以前的日志在每天凌晨5:00转储到日志服务器
3、脚本转储失败,运维人员需要通过跳板机的菜单选择手动清理日志
1)apache日志每天进行轮转:
vim /usr/local/apache2/conf/extar/http-vhosts.conf
...
ErrorLog "|/usr/local/apache2/bin/rotatelogs -l /usr/local/apache2/logs/error_log-%Y%m%d 86400"
CustomLog "|/usr/local/apache2/bin/rotatelogs -l /usr/local/apache2/logs/access_log-%Y%m%d 86400" common
...
说明:
1.rotatelogs程序是apache自带的日志切割工具,-l参数表示使用本地系统时间为标准切割,不是GMT时区时间
2./usr/local/apache2/logs/access_log-%Y%m%d 86400用来指定日志文件位置和名称,其中86400用来指定分割时间默认单位为s,24小时制
2)log-server上搭建rsync,ip 10.1.1.2
cat /etc/rsyncd.conf
[web1]
path = /web1/logs
uid = root
gid = root
read only = false
[web2]
path = /web2/logs
uid = root
gid = root
read only = false
echo rsync --daemon >> /etc/rc.local
3)web服务器上定义清理脚本
#!/bin/bash
#clean log
clean_log(){
remote_log_server=10.1.1.2
server=$1
log_dir=/usr/local/apache2/logs
log_tmp_dir=/tmp/log
host=`ifconfig eth0|sed -n '2p'|awk -F'[ :]+' '{print $4}'`
[!-d $log_tmp_dir] && mkdir -p $log_tmp_dir
cd $log_dir
find ./ -daystart -mtime +3 -exec tar -uf $log_tmp_dir/`echo $host`_$(date +%F).tar{}\;
find ./ -daystart -mtime +3 -delete
cd $log_tmp_dir
rsync -a ./ $remote_log_server::$server && find ./ -daystart -mtime +1 -delete
}
4)jumper-server
#!/bin/bash
#jumper-server
#菜单打印
trap '' 1 2 3
menu1(){
cat <<-END
请选择对web1的操作动作:
1.clean_apache_log
2.reload_apache_service
3.test_apache_service
4.remote login
END
}
menu2(){
cat <<-END
请选择需要操作的主机:
1.DB1-Master
2.DB2-Slave
3.Web1
4.Web2
5.exit
END
}
push_pubkey(){
ip=$1
#判断公钥文件是否存在,没有就生成公钥
[!-f ~/.ssh/id_rsa.pub] && ssh-keygen -P "" -f ~/.ssh/id_rsa &>/dev/null
#安装expect程序,与交互程序对话
sudo rpm -q expect &>/dev/null
test $? -ne 0 && sudo yum -y install expect
#将跳板机上yunwei用户的公钥推送到指定服务器上
/usr/bin/expect <<-EOF
spawn ssh-copy-id -i root@$ip
expect{
"yes/no" {send "yes\r";exp_continue}
"password:" {send "123456\r"}
}
expect eof
EOF
}
while true
do
menu2
#让用户选择相应操作
read -p "输入需要操作的主机:" host
case $host in
1)
ssh [email protected]
;;
2)
ssh [email protected]
;;
3)
clear
menu1
read -p "请输入需要操作的动作:" action
case $action in
1)
ssh [email protected] clean_log web1
test $? -eq 0 && ech "日志清理完毕..."
;;
2)
service apache reload
;;
3)
wget http://10.1.1.1 &>/dev/null
test $? -eq 0 && echo "web服务正常运行..."|| echo "web服务无法访问,请检查..."
;;
4)
ssh [email protected]
;;
"")
:
;;
esac
;;
5)
exit
;;
*)
clear
echo "输入错误,请重新输入..."
;;
esac
done