AI智能
改变未来

shell脚本学习——正则表达式、sed、awk

一、正则表达式
正则表达式用于查找匹配指定的字符
支持正则表达式的程序: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  或  egrepsed -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.txtsed -n \'2p\' a.txtsed -n \'1,5p\' a.txtsed -n \'$p\' a.txt增加文件内容sed \'2ihello world\' a.txtsed \'3ihello\\nworld\' a.txtsed \'2,4a999\' a.txt修改文件内容sed \'$chello world\' a.txtsed \'/^adm/chello world\' a.txtsed \'1,5chello world\' a.txt  将1~5行替换为一行hello world删除文件内容sed \'1,4d\' a.txtsed -r \'/([0-9]{1,3}\\.){3}[0-9]{1,3}/d\' a.txt

对文件进行搜索替换操作

语法:sed 选项 ‘s/搜索的内容/替换的内容/动作’ 需要处理的文件s表示搜索;/表示分隔符,可自定义;动作有打印p和全局替换gsed -n \'s/root/ROOT/p\' 1.txtsed -n \'s/root/ROOT/gp\' 1.txtsed -n \'s/^#//gp\' 1.txt  去掉#号sed -n \'s@/sbin/nologin@hello@gp\' 1.txtsed -n \'10s@/sbin/nologin@hello@gp\' 1.txt 替换第10行sed -n \'1,5s/^/#/gp\' 1.txtsed -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.txtsed -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.confsed -e \'/^#/d;/^$/d\' vsftpd.confsed -r \'/^#|^$/d\' vsftpd.confsed -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 -f1,5ds/root/ROOT/g3i7775i888a999p注意:脚本文件是一个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 filenameawk -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和printfprint函数    相当于echodate |awk \'{print \"Month: \"$2 \"\\nYear: \"$NF}\'printf函数   相当于echo -nawk -F: \'{printf \"%-15s %-10s %-15s\\n\", $1,$2,$3}\' /etc/passwdawk -F: \'{printf \"|%-15s| %-10s| %-15s|\\n\", $1,$2,$3}\' /etc/passwdawk变量定义awk -v NUM=3 -F: \'{print NUM}\' 1.txtawk -v NUM=3 \'BEGIN{print NUM}\'#awk中调用定义的变量不需要加$awk中的BEGIN...ENDBEGIN表示程序开始前执行END表示所有文件处理完后执行\'BEGIN{开始处理前};{处理中}:END{处理结束后}\'例:打印最后一列和倒数第二列awk -F: \'BEGIN{print \"Login_shell\\t\\tLogin_home\\n*************\"};{print $NF\"\\t\\t\"$(NF-1)};END{print \"*************\"}\' 1.txt2、打印/etc/passwd里的用户名、家目录、登录shellawk -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/passwdawk与正则运用==        等于!=        不等于>         大于<         小于>=        大于等于<=        小于等于~         匹配!~        不匹配!         逻辑非&&        逻辑与||        逻辑或awk -F: \'NR==1,/^lp/{print $0}\' passwdawk -F: \'/^root/ || /^lp/{print $0}\' passwdawk \'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\"是管理员\"}}\' passwdawk \'BEGIN{if($(id -u)==0) {print \"admin\"}}\'if...else结构格式:{if(表达式) {语句;语句;...} else {语句;语句;...}}awk -F: \'{if($3>=500 && $3 !=65534) {print $1\"是普通用户\"} else {print $1\"不是普通用户\"}}\' passwdawk \'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\"}}\' passwdawk -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}}\'11212312341234599乘法口诀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练习——统计

统计系统中的shellawk -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]}}\'|sortss -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|headgrep \'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.2cat /etc/rsyncd.conf[web1]path = /web1/logsuid = rootgid = rootread only = false[web2]path = /web2/logsuid = rootgid = rootread only = falseecho rsync --daemon >> /etc/rc.local3)web服务器上定义清理脚本#!/bin/bash#clean logclean_log(){remote_log_server=10.1.1.2server=$1log_dir=/usr/local/apache2/logslog_tmp_dir=/tmp/loghost=`ifconfig eth0|sed -n \'2p\'|awk -F\'[ :]+\' \'{print $4}\'`[!-d $log_tmp_dir] && mkdir -p $log_tmp_dircd $log_dirfind ./ -daystart -mtime +3 -exec tar -uf $log_tmp_dir/`echo $host`_$(date +%F).tar{}\\;find ./ -daystart -mtime +3 -deletecd $log_tmp_dirrsync -a ./ $remote_log_server::$server && find ./ -daystart -mtime +1 -delete}4)jumper-server#!/bin/bash#jumper-server#菜单打印trap \'\' 1 2 3menu1(){cat <<-END请选择对web1的操作动作:1.clean_apache_log2.reload_apache_service3.test_apache_service4.remote loginEND}menu2(){cat <<-END请选择需要操作的主机:1.DB1-Master2.DB2-Slave3.Web14.Web25.exitEND}push_pubkey(){ip=$1#判断公钥文件是否存在,没有就生成公钥[!-f ~/.ssh/id_rsa.pub] && ssh-keygen -P \"\" -f ~/.ssh/id_rsa &>/dev/null#安装expect程序,与交互程序对话sudo rpm -q expect &>/dev/nulltest $? -ne 0 && sudo yum -y install expect#将跳板机上yunwei用户的公钥推送到指定服务器上/usr/bin/expect <<-EOFspawn ssh-copy-id -i root@$ipexpect{\"yes/no\" {send \"yes\\r\";exp_continue}\"password:\" {send \"123456\\r\"}}expect eofEOF}while truedomenu2#让用户选择相应操作read -p \"输入需要操作的主机:\" hostcase $host in1)ssh [email protected];;2)ssh [email protected];;3)clearmenu1read -p \"请输入需要操作的动作:\" actioncase $action in1)ssh [email protected] clean_log web1test $? -eq 0 && ech \"日志清理完毕...\";;2)service apache reload;;3)wget http://10.1.1.1 &>/dev/nulltest $? -eq 0 && echo \"web服务正常运行...\"|| echo \"web服务无法访问,请检查...\";;4)ssh [email protected];;\"\"):;;esac;;5)exit;;*)clearecho \"输入错误,请重新输入...\";;esacdone
赞(0) 打赏
未经允许不得转载:爱站程序员基地 » shell脚本学习——正则表达式、sed、awk