Linux之正则表达式

基本正则匹配字符

Linux之正则表达式
如:3,9s/(.*)/#\1/
\:用来转义元字符
<:词首定位符
>:词尾定位符

扩展正则

Linux之正则表达式
(…)(…)\1\2 标签匹配字符
\w:所有字母与数字,字符[a-zA-Z0-9]
\W:所有除字母与数字之外的字符,非字符[^a-zA-Z0-9]

Grep/egrep使用

1)利用egrep工具练习正则表达式的基本用法
grep命令不带-E选项时,支持基本正则匹配模式。比如“word”关键词检索、“^word”匹配以word开头的行、“word$”匹配以word结尾的行……等等。

grep '^r' /etc/passwd
grep 'localhost$' /etc/hosts
grep '^root|^daemon' /etc/passwd

而若使用grep -E或egrep命令,可支持扩展正则匹配模式,能够自动识别 |、{} 等扩展正则表达式中的特殊字符,用起来更加方便,比如:

grep -E '^(root|daemon)' /etc/passwd

使用grep -E 与 使用egrep命令完全等效,推荐使用后者,特别是涉及到复杂的正则表达式的时候。
2)grep、egrep命令的-q选项
选项 -q 表示 quiet(静默)的意思,结合此选项可以只做检索而并不输出,通常在脚本内用来识别查找的目标是否存在,通过返回状态 $? 来判断,这样可以忽略无关的文本信息,简化脚本输出使用 -q 选项的效果与使用 &> /dev/null的效果类似。
3)基本元字符 ^、$ —— 匹配行首、行尾
统计本地用户中登录Shell为“/sbin/nologin”的用户个数:
提示:-m10仅在文件的前10行中过滤,后面的行不再过滤。

egrep -m10 '/sbin/nologin$' /etc/passwd  //先确认匹配正确

使用 -c 选项可输出匹配行数,这与通过管道再 wc -l的效果是相同的,但是写法更简便。比如,统计使用“/bin/bash”作为登录Shell的正常用户个数,可执行: egrep -c '/bin/bash$' /etc/passwd
输出/etc/rc.local文件内的空行(用 –v 选项将条件取反):egrep -v '.' /etc/rc.local grep '^$' /etc/rc.local
5)基本元字符 +、?、* —— 目标出现的次数
+:最少匹配一次,比如a+可匹配a、aa、aaa等
?:最多匹配一次,比如a?可匹配零个或一个a
*:匹配任意多次,比如a*可匹配零个或任意多个连续的a
6)元字符 {} —— 限定出现的次数范围
输出包括ababab的行,即“ab”连续出现3次:egrep ‘(ab){3}’ brace.txt
7)元字符 [] —— 匹配范围内的单个字符
输出包括以“ll”结尾的单词的行,使用 > 匹配单词右边界:egrep 'll\>' /etc/rc.local9多个条件的组合,通过dmesg启动日志查看蓝牙设备、网卡设备相关的信息: egrep -i 'eth|network|bluetooth' /var/log/dmesg

sed基本用法:(-i\-r\-n\p\s\d\a\i\c\g)

sed不管搜索的是不是自己查找的结果,$?返回的状态都是0,只有出现语法错误时,返回的是1。
sed文本处理工具的用法:
用法1:前置命令 | sed [选项] ‘条件指令’
用法2:sed [选项] ‘条件指令’ 文件… …
条件可以是行号或者/正则/
没有条件时,默认为所有条件
指令可以是增、删、改、查等指令
默认sed会将所有输出的内容都打印出来,可以使用-n屏蔽默认输出
选项中可以使用-r选项,让sed支持扩展正则
Sed -nri ‘1~2d’ 文件奇数行删除
Sed -nri ‘0~2d’偶数行删除
%s/root/&yyyy/g 把全文匹配的root后加上yyyy &代表查找字符串中匹配到的内容
Sed -r ‘/(.*)/#\1’ password 添加注释
Sed -r ‘s/(.)(.)(.*)/\1yyy\2\3’ passwd
Sed -r ‘/root/w /tmp/1.txt’ passwd 将匹配到的root行写到tmp/1.txt中去
Sed -r ‘/eastern/{n;s/AM/Archile}’ datafile 匹配到的行的下一个操作
模式空间和暂存空间:
Linux之正则表达式

h:把模式空间的内容覆盖到暂存空间
H:把模式空间的内容追加到暂存空间
g:取出暂存空间的内容覆盖到模式空间
G:取出暂存空间的内容追加到模式空间
x:交换暂存空间与模式空间的内容

Sed -r ‘1h;$G’ /etc/hosts
Sed -r ‘1{h;d};$G’ /etc/hosts
Sed -r ‘1h;2,$g’ /etc/hosts
Sed -r ‘1h;2,3H;$G’ /etc/hosts
Sed -r ‘4h;5x;6G’ /etc/hosts

删除配置文件中#号注释

Sed -ri ‘/^[ \t]*#/d’  /etc/vsftpd/vsftpd.conf

删除配置文件中//号注释行

Sed -ri ‘\#^[ \t]*//#d’ /etc/vsftpd/vsftpd.conf

删除配置文件中的空行

Sed -ri ‘/^[ \t]*$/d’ /etc/vsftpd/vsftpd.conf

给文件添加注释

Sed -r ‘30,50s/^[ \t]*#*/#/’ a.txt
sed -r '3,5s/(.*)/#\1/' /etc/passwd

去掉//注释

sed -r 's/^\/\/(.*)/\1/' /etc/passwd

应用变量

Sed -ri ‘3a$var1’ /etc/hosts
Sed -ri ‘3a’”$var1” /etc/hosts

sed命令的常用选项如下:
-n(屏蔽默认输出,默认sed会输出读取文档的全部内容)
-r(让sed支持扩展正则)
-i(sed直接修改源文件,默认sed只是通过内存临时修改文件,源文件无影响)
1)sed命令的 -n 选项
执行p打印等过滤操作时,希望看到的是符合条件的文本。但不使用任何选项时,默认会将原始文本一并输出,从而干扰过滤效果。sed '1p' /etc/hosts sed -n '1p' /etc/hosts
行号可以是连续的行号sed -n '3,6p' /etc/passwd
2)sed命令的 -i 选项
正常情况下,sed命令所做的处理只是把操作结果(包括打印、删除等)输出到当前终端屏幕,而并不会对原始文件做任何更改:sed 'd' /etc/passwd //删除所有行
若希望直接修改文件内容,应添加选项 -i 。
3)多个指令可以使用分号隔离
sed -n '1p;4p' /etc/passwd
注意:替换操作的分隔“/”可改用其他字符,如#、&等,便于修改文件路径
使用“()”可实现保留功能sed -r 's/([A-Z])/[\1]/g' nssw.txt
sed工具的多行文本处理操作:
i: 在指定的行之前插入文本
a:在指定的行之后追加文本
c:替换指定的行
root@svr5 ~]# sed '2a XX' a.txt //在第二行后面,追加XX
[root@svr5 ~]# sed '2i XX' a.txt //在第二行前面,插入XX
[root@svr5 ~]# sed '2c XX' a.txt //将第二行替换为XX

awk基本用法

awk文本过滤的基本用法:
和sed一样是按行处理文件
$0:当前记录的内容
NR:所有文件记录一起编号
FNR:多个文件记录分别编号
FS:输入字段分隔符
OFS:输出字段分隔符
NF:当前记录的列数
RS:输入记录分隔符
ORS:输出记录分隔符
1)基本操作方法
格式:awk [选项] ‘[条件]{指令}’ 文件
其中,print 是最常用的编辑指令;若有多条编辑指令,可用分号分隔。
Awk过滤数据时支持仅打印某一列,如第2列、第5列等。
处理文本时,若未指定分隔符,则默认将空格、制表符等作为分隔符。
直接过滤文件内容:awk '{print $1,$3}' test.txt
结合管道过滤命令输出:df -h | awk '{print $4}'
2)选项 -F 可指定分隔符
输出passwd文件中以分号分隔的第1、7个字段,显示的不同字段之间以逗号隔开, awk -F: '{print $1,$7}' /etc/passwd
awk还识别多种单个的字符,比如以“:”或“/”分隔,输出第1、10个字段:awk -F [:/] '{print $1,$10}' /etc/passwd
awk常用内置变量:
$0 文本当前行的全部内容
$1 文本的第1列
$2 文件的第2列
$3 文件的第3列,依此类推
NR 文件当前行的行号
NF 文件当前行的列数(有几列)
输出每次处理行的行号,以及当前行以“:”分隔的字段个数(有几列):awk -F: '{print NR,NF}' passwd.txt
3)awk的print指令不仅可以打印变量,还可以打印常量awk -F: '{print $1,"的解释器:",$7}' /etc/passwd
利用awk提取本机的网络流量、根分区剩余容量、获取远程失败的IP地址:
1)提取IP地址
RX为接收的数据量,TX为发送的数据量。packets以数据包的数量为单位,bytes以字节为单位
ifconfig eth0 | awk '/RX p/{print $5}' //过滤接收数据的流量
ifconfig eth0 | awk '/TX p/{print $5}' //过滤发送数据的流量
2)提取根分区剩余容量

df -h / | tail -1 | awk '{print $5}'

或者直接在awk中使用正则:
3)根据/var/log/secure日志文件,过滤远程连接密码失败的IP地址

awk '/Failed/{print $11}' /var/log/secure

格式化输出/etc/passwd文件:
awk会逐行处理文本,支持在处理第一行之前做一些准备工作,以及在处理完最后一行之后做一些总结性质的工作。

awk  [选项]  '[条件]{指令}'  文件
awk  [选项]  ' BEGIN{指令} {指令} END{指令}'  文件

BEGIN{ } 行前处理,读取文件内容前执行,指令执行1次
{ } 逐行处理,读取文件过程中执行,指令执行n次
END{ } 行后处理,读取文件结束后执行,指令执行1次
举个例子(统计系统中使用bash作为登录Shell的用户总个数):
a.预处理时赋值变量x=0
b.然后逐行读入/etc/passwd文件,如果发现登录Shell是/bin/bash则x加1
c.全部处理完毕后,输出x的值即可。相关操作及结果如下:

awk 'BEGIN{x=0} /bash$/{x++} END{print x}' /etc/passwd

格式化输出/etc/passwd文件
要求: 格式化输出passwd文件内容时,要求第一行为列表标题,中间打印用户的名称、UID、家目录信息,最后一行提示一共已处理文本的总行数

awk -F: 'BEGIN{print "User\tUID\tHome"}{print $1 "\t"  $3  "\t"  $6}END{print "Total",NR,"lines."}' /etc/passwd
Awk -F: ‘{printf “%-15s %-10s %-15s”,$1,$2,$3}’ /etc/passwd

%s字符类型%f浮点类型%d数字类型-左对齐,printf默认不会在行尾换行
awk处理条件:
认识awk处理条件的设置:
1)使用正则设置条件

awk -F: '/bash$/{print}' /etc/passwd
awk -F: '/root/' /etc/passwd
awk -F: '/^(root|adm)/{print $1,$3}' /etc/passwd

输出账户名称包含root的基本信息(第1列包含root)的行:

awk -F: '$1~/root/' /etc/passwd

输出其中登录Shell不以nologin结尾(对第7个字段做!~反向匹配)的用户名、登录Shell信息:awk -F: '$7!~/nologin$/{print $1,$7}' /etc/passwd
2)使用数值/字符串比较设置条件
比较符号:==(等于) !=(不等于) >(大于)
>=(大于等于) <(小于) <=(小于等于)
输出第3行(行号NR等于3)的用户记录:

awk -F: 'NR==3{print}' /etc/passwd

输出账户UID大于等于1000的账户名称和UID信息:

awk -F: '$3>=1000{print $1,$3}' /etc/passwd

输出用户名为“root”的行:

Awk -F: '$1=="root"' /etc/passwd

3)逻辑测试条件
输出账户UID大于10并且小于20的账户信息:

awk -F: '$3>10 && $3<20' /etc/passwd

4)数学运算

awk 'BEGIN{x++;print x}'
seq  200 | awk  '$1%3==0'

完成任务要求的awk过滤操作

awk -F: '$3>=1 && $3<=1000' /etc/passwd

列出100以内整数中7的倍数或是含7的数:

seq 100 | awk '$1%7==0||$1~/7/'

awk综合脚本应用:

[root@svr5 ~]# cat getupwd-awk.sh
#/bin/bash
A=$(awk -F:  '/bash$/{print $1}' /etc/passwd) ## 提取符合条件的账号记录
for i in $A
do
        grep $i /etc/shadow | awk -F: '{print $1,"-->",$2}'                
done

awk流程控制:
{if(表达式){语句;语句;…}}

Awk -F: ‘{if($3>0&&$3<100){count++}}END{print count}’ /etc/passwd

{if(表达式){语句;语句;…}else{语句;语句;…}}

Awk -F: ‘{if($3==0){count++}else{i++}}END{print count;print i}’ /etc/passwd

{if(表达式){语句;语句;…}else if(表达式){语句;语句;…}else{语句}}

Awk -F: ‘{if($3==0){i++}else if($3>999){k++}else{j++}}END{print i;print k; print j}’ /etc/passwd

While 循环

awk ‘{i=1;while(i<=NF){print $i;i++}}’ b.txt

for循环

awk ‘{for(i=1;i<=NF;i++){print $i}}’ passwd

数组

Awk -F: ‘{username[i++]=$1}END{print username[0]}’/etc/passwd
Awk -F: ‘{username[x++]=$1}END{for( i in username){print i,username[i]}}’/etc/passwd

awk统计Web访问排名
在分析Web日志文件时,每条访问记录的第一列就是客户机的IP地址,其中会有很多重复的IP地址。因此只用awk提取出这一列是不够的,还需要统计重复记录的数量并且进行排序。
通过awk提取信息时,利用IP地址作为数组下标,每遇到一个重复值就将此数组元素递增1,最终就获得了这个IP地址出现的次数。
针对文本排序输出可以采用sort命令,相关的常见选项为-r、-n、-k。其中-n表示按 数字顺序升序排列,而-r表示反序,-k可以指定按第几个字段来排序。
统计Web访问量排名:
提取IP地址及访问量

awk '{ip[$1]++}END{for(i in ip) {print ip[i],i }}' /var/log/httpd/access_log
awk  '{ip[$1]++}END{for(i in ip) {print ip[i],i}}' /var/log/httpd/access_log | sort -nr
Awk -F: ‘{if($3>300){print $3}}else{print $1}’ /etc/passwd

awk使用外部变量
方法一:在双引号的情况下用

Var=“bash”
Echo “unix script”|awk “gsub(/unix/,\”$var\”)”

方法二:在单引号的情况下用
Var=“bash”
Echo “unix script”|awk ‘gsub(/unix/,”’”$var”’”)’
方法三:使用-v

Echo “unix script”|awk -v var=”bash”’/gsub(/unix/,var)’

shell脚本中使用多个解释器,只有在bash中才能识别这种结构

#!/uer/bin/bash
echo "hello tom!" 
 /usr/bin/python <<-EOF
print "hello world!"
print "hello world!"
print "hello world!"
EOF

开关闭解释器时执行的文件
/etc/profile /etc/bashrc 系统级
~/.bash_profile ~/.bashrc 用户级 进入shell时执行
~/.bash_logout ~/.bash_history 退出时执行
输出颜色

echo -e “\e[1;31m内容\e[0m”
Echo -e “\033[1;31m内容\033[0m”

次方运算

env  echo $[2**2] 2的2次方
echo "2^2" |bc 2的2次方

以调试的方式执行bash -vx 脚本 bash -n 脚本 查看是否有语法问题
i++ 先赋值再运算 ++i 先运算再赋值
在条件匹配中单[]不支持正则,[[]]才支持正则if [[ ! " n u m " =   [ 0 − 9 ] + num" =~ ^[0-9]+ num"= [0−9]+’’ ]]
if条件句不做任何操作可以用:代替 if command -v $command1 &>/dev/null;then : fi
trap "" HUP INT OUIT TSTP 捕捉键盘信号不做任何操作
[ ${#line} -eq 0 ]判断变量长度
希望for处理文件按回车分隔,而不是空格或tab空格,IFS内部字段分隔符

IFS=$'\n'或
IFS=‘
’

for 是按tab 空格 回车分隔 while 是按行分割

应用实例

系统工具箱:

#!/bin/bash
#system manager
#v1.0 by jerome 2019-8-23
menu () {
cat <<-EOF
                   meum
############################################
#        h.help                            #
#        f.disk partition                  #  
#        d.filesystem mount                #
#        m.memory                          #
#        u.system load                     #
#        q.exit                            #
############################################
EOF
}
menu
while :
do
        read -p "please input [h for help]:" action
        case $action in
        h) clear;menu;;
        help) clear;menu;;
        f) fdisk -l;;
        d) df -Th;;
        m) free -h;;
        u) uptime;;
        "") ;;
        *) break
        esac
done
echo "finish..."

ping检测

#!/bin/bash
#ping check
>ip.txt
for i in {2..254}
do
{
	ip=192.168.122.$i
	ping -c1 -w1 $ip &>/dev/null
if [ $? -eq 0 ];then
	echo "$ip" |tee -a ip.txt
fi
}&
done
wait
echo "finish..."

批量创建用户

#!/bin/bash
while :
do
read -p "please enter prefix & pass & num[jerome 123 5]: " prefix pass num
printf "user infomation:
---------------------------------------
user prefix: $prefix
user password: $pass
user number: $num
---------------------------------------
"
read -p "are you sure?[y/n]: " action
if [ "$action" == "y" ];then
break
fi
done
for i in `seq -w $num`
do
   user=$prefix$i
   id $user &>/dev/null
   if [ $? -eq 0 ];then
       echo "user $user already exists"
   else
        useradd $user
        echo "$pass" |passwd --stdin $user &>/dev/null
       if [ $? -eq 0 ];then
         echo "$user is created"
       fi
   fi
done

实现批量主机修改密码

#!/bin/bash
#v1.0 by jerome 2019-8-24
read -p "please enter a new password: " pass
for ip in $(cat ip.txt)
do
        {
        ping -c1 -w1 $ip &>/dev/null
        if [ $? -eq 0 ];then
           ssh $ip "echo $pass |passwd --stdin root"
           if [ $? -eq 0 ];then
               echo "$ip" >>ok_`date +%F`.txt
           else
               echo "$ip" >>fail_`date +%F`.txt
           fi
        else
          echo "$ip" >>fail_`date +%F`.txt
        fi
         }&
done
wait
echo "finish..."

查看文件描述符

进程使用文件描述符来管理打开的文件,查看进程的文件描述符ll /proc/进程号/fd
touch /file1 将文件添加到进程中exec 6<> /file1 ll /proc/$$/fd
echo ssss >> /proc/$$/fd/6 cat /file1 rm -rf /file1 ll /proc/$$/fd
cp /proc/$$/fd/6 /file1 cat /file1 ll /proc/$$/fd 去除进程中的文件描述符
exec 6<&- ll /proc/$$/fd
当一个文件fd未被释放,删除原文件也不会影响fd

管道

匿名管道 rpm -qa |grep bash
命名管道 先进先出,出了就没了 创建一个管道文件 mkfifo /tmp/tmpfifo file /tmp/tmpfifo
shell实现并发控制

#!/bin/bash
#ping check
#v1.0 by jerome 2019-8-24
thread=5
tmp_fifofile=/tmp/$$.fifo
mkfifo $tem_fifofile //创建一个管道
exec 8<> $tem_fifofile //添加文件描述符
rm $tmp_fifofile
for i in `seq $thread`
do  
   echo  >&8  //随便输入什么
done
for i in {1..254}
do
	read -u 8 //读取这个管道
	{
	ip=192.168.122.$i
	ping -c1 -w1 $ip &>/dev/null
	if [ $? -eq 0 ];then
	   echo "$ip is up."
	else  
	   echo "$ip is down"
	fi
	echo >&8
	}&
done
wait
exec 8>&-  释放文件描述符
echo "finish..."
上一篇:Linux-shell-AWK


下一篇:温故知新,基础复习(快速排序及优化)