1、正则表达式
a. 概念
正则表达式(或称Regular Expression,简称RE),是用于描述字符排列和匹配模式的一种语法规则。它主要用于字符串的分割,匹配、査找及替换操作。即正则表达式是一种文本模式,该模式描述在搜索文本时要匹配的一个或多个字符串。
b. 工作流程
正则表达式是用来匹配文件中的字符串的方法。它会先把个文整本分成一行一行的字符串,然后从每行字符串中搜索是否有符合正则表达式规则的字符串,如果有则匹配成功,如果没有则匹配失败。
c. 正则与通配符
正则表达式用来在文件中匹配符合条件的字符串,通配符用来匹配符合条件的文件名, 这种区别只在 Shell 中适用,因为用来在文件中搜索字符串的命令,如 grep、awk、sed、vi 等可以支持正则表达式,而在系统中搜索文件的命令,如 ls、find、cp 等不支持正则表达式,所以只能使用 Shell 自己的通配符来进行匹配。
d. 基础元字符
在正则表达式中,我们把用于匹配的特殊符号又称作元字符。在shell中,元字 符又分为基础元字符(BRE)和扩展元字符(ERE)。
* | 匹配前面的子表达式零次或多次。 |
. | 匹配除换行符(\n、\r)之外的任何单个字符 |
.* | 匹配任意长度字符串 |
^ | 匹配行首。^hello 会匹配以 hello 开头的行 |
$ | 匹配行尾。hello$ 会匹配以 hello 结尾的行 |
^& | 匹配空行 |
[] | 匹配中括号中指定的任意一个字符,而且只匹配一个字符。[0-9] 匹配任意一位数字, [a-zA-Z] 匹配任意一位英文字母 |
[^] | 匹配除了中括号字符以外的任意一个字符,[^ab^c] 匹配除了a、b、^、c以外的任意单个字符 |
\ | 转义符,用于取消特殊符号的含义,使该特殊字符成为普通字符。 |
{n} | 匹配连续出现字符的行。n 是一个非负整数。匹配确定的 n 次。例如,'o{2}' 不能匹配 "Bob" 中的 'o',但是能匹配 "food" 中的两个o |
{n,} | 匹配连续出现字符的行。n 是一个非负整数。至少匹配n 次。例如,'o{2,}' 不能匹配 "Bob" 中的 'o',但能匹配 "foooood" 中的所有o。 |
{n,m} | 匹配连续出现字符的行。m 和 n 均为非负整数,其中n <= m。最少匹配 n 次且最多匹配 m 次。例如,"o{1,3}" 将匹配 "fooooood" 中的前三个o |
\d | 匹配一个数字字符。等价于[0-9] |
\D | 匹配一个非数字字符。等价于[^0-9] |
\w | 匹配字母、数字、下划线。等价于'[A-Za-z0-9_]' |
\W | 匹配任何非单词字符。等价于“[^A-Za-z0-9_]” |
\b | 匹配一个单词边界。'er\b' 可以匹配"never" 中的 'er',但不能匹配 "verb" 中的 'er' |
\B | 匹配单词非边界。'er\B' 能匹配 "verb" 中的 'er',但不能匹配 "never" 中的 'er' |
\< \> | 匹配词(word)的开始(\<)和结束(\>),例如正则表达式\<the\>能够匹配字符串"for the wise"中的"the",但是不能匹配字符串"otherwise"中的"the"。注意:这个元字符不是所有的软件都支持的。 |
e. 扩展元字符
正则表达式除了支持基础元字符,还支持"+"、"?"、"|"、"()"等元字符,默认linux是支持这些元字符,只不过grep不支持,如果grep想要使用这些元字符,可以使用egrep 或 grep -E 命令。
字符 | 描述 |
+ | 匹配前面的一个字符或子表达式1次或任意多次,'zo+' 能匹配 "zo" 以及 "zoo",但不能匹配 "z"。等价于 {1,} |
? | 匹配前面的一个字符或子表达式零次或一次,"do(es)?" 可以匹配 "do"或"does"。? 等价于{0,1} |
| | 表示或,“was|his”既会匹配包含“was”的行,或匹配包含“his”的行 |
() | 标记一个子表达式的开始和结束位置,将括号里的内容看成是一个整体,“(dog)+”会匹配“dog” “dogdog” “dogdogdog”等 |
f. 非打印字符
非打印字符也可以是正则表达式的组成部分
字符 | 描述 |
\cx | 匹配由x指明的控制字符。例如, \cM 匹配一个 Control-M 或回车符。x 的值必须为 A-Z 或 a-z 之一。否则,将 c 视为一个原义的 'c' 字符。 |
\f | 匹配一个换页符。等价于\x0c 和 \cL。 |
\n | 匹配一个换行符。等价于\x0a 和 \cJ。 |
\r | 匹配一个回车符。等价于\x0d 和 \cM。 |
\t | 匹配一个制表符。等价于\x09 和 \cI。 |
\v | 匹配一个垂直制表符。等价于\x0b 和 \cK |
\s | 匹配任何空白字符,包括空格、制表符、换页符等等。等价于[ \f\n\r\t\v]。 |
\S | 匹配任何非空白字符。等价于[^ \f\n\r\t\v]。 |
g. 修饰符(标记)
正则表达式的标记用于指定额外的匹配策略, 标记不写在正则表达式里,标记位于表达式之外
字符 | 描述 |
i | 将匹配设置为不区分大小写,搜索时不区分大小写: A 和 a 没有区别 |
g | 查找所有的匹配项 |
m | 使边界字符^ 和 $ 匹配每一行的开头和结尾,记住是多行,而不是整个字符串的开头和结尾 |
s | 默认情况下的圆点. 是匹配除换行符 \n 之外的任何字符,加上 s 修饰符之后, . 中包含换行符 \n。 |
h. 字符优先级
正则表达式从左到右进行计算,并遵循优先级顺序,这与算术表达式非常类似,相同优先级的从左到右进行运算,不同优先级的运算先高后低。
字符 | 描述 | |
\ | 转义符 | |
(), (?:), (?=), [] | 圆括号和方括号 | |
*, +, ?, {n}, {n,}, {n,m} | 限定符 | |
^, $, \任何元字符、任何字符 | 位置和顺序 | |
| | "或"操作 |
2、三剑客之 grep
- 描述:过滤
- 语法:grep [选项] 字符
-E, --extended-regexp PATTERN 是一个可扩展的正则表达式(缩写为 ERE)
-G, --basic-regexp PATTERN 是一个基本正则表达式(缩写为 BRE)
-P, --perl-regexp PATTERN 是一个 Perl 正则表达式
-n 显示匹配行的行号
-v 显示不包含关键字的行
-i 忽略大小写
-l 一般和-r联用, 只显示包含关键字的文件的名字, 而不是显示文件内容
-r 递归检索
-w 按照单个词语的方式匹配
# 过滤掉空行和注释行
grep -vE "^#|^$" test.txt
# 过滤以数字结尾的行
grep -E "[0-9]$" test.txt | grep -P "\d$" test.txt
# 过滤出以字母开头的行
grep -E "^[a-zA-Z]" test.txt | grep -iE "^[a-z]" test.txt
# 过滤出带 the 的行。
grep -E "(the)" test.txt
# 过滤出 the 单词
grep -E "\<the\>" test.txt
# 过滤 连续出现2次d 的行
grep -E "d{2}" test.txt
# 过滤出 至少连续出现3次r 的行
grep -E "r{3,}" test.txt | grep -P "r{3,}" test.txt
# 过滤手机号
grep -E "\<1[3|4|5|6|7|8][0-9]{9}\>" phone.txt
3、三剑客之 sed
- 描述:采用的是流编辑模式,无交互式的方式来编辑文本。
- 语法:sed [选项] [动作指令] filename
选项:
-n 默认情况下,sed 会在动作指令执行完毕后,自动输出处理后的内 容, 而该选项会屏蔽默认输出。
-e 执行多个sed指令
-i 此选项会直接修改源文件,要慎用,修改前建议先备份源文件。
-i.bak 编辑源文件的同时创造.bak的备份
-r 使用扩展的正则表达式
动作指令:
p 打印,输出指定的行
s 替换,替换指定字符串
d 删除,删除行
a 增加行,在当前行下面插入文件
i 增加行,在当前行上面插入文件
c 把选定的行改为新的指定的文本
r 读取文件,即用于将一个独立文件的数据插入到当前数据流的指定位置
w 另存为
注意:搜索条件要使用“/…/”括起来
-----------------------------------------
我们知道,vim/vi 采用的是交互式文本编辑模式,你可以用键盘命令来交互性地插入、删除或替换数据中的文本。
sed全名叫stream editor,流编辑器。它采用的是流编辑模式,最明显的特点是,在sed 处理数据之前,需要预先提供一组规则,sed会按照此规则来编辑数据,实现无交互式编辑数据。
sed也是支持正则表达式的,如果要使用扩展正则加参数-r
sed编辑器逐行处理文件(或输入),并将结果发送到屏幕。
- 首先sed把当前正在处理的行保存在一个临时缓存区中(也称为模式空间)。
- 然后处理临时缓冲区中的行,完成后把该行发送到屏幕上。
- sed每处理完一行就将其从临时缓冲区删除,然后将下一行读入,进行处理和显示。
- 处理完输入文件的最后一行后,sed便结束运行。
a. 打印_p
# 查询test1.txt文件cat这一行
sed '/cat/p' test1.txt
查询test1.txt文件cat这一行,禁止默认输出
sed -n '/cat/p' test1.txt
- p指令是默认输出所有行,"-n"选项,禁止默认输出
# 查看test1.txt文件第2行
cat -n test1.txt | sed -n '2p'
# 一次打印多行
cat -n test1.txt | sed -n -e '2p' -e '4,6p'
b. 替换_s
- 语法:sed 's/要被替换的字串/新的字串/g' 文件
- 注意:sed 默认不会直接修改源文件数据,想要修改源文件请添加-i选项
# 把test1.txt文件中的name替换为names
sed 's/name/James/g' test1.txt
# 将原文件中的name全部替换为 Tom
sed -i 's/name/Tom/g' test1.txt
# 查看原文件
cat test1.txt
# 注释test1.txt第4-6行
sed -i '4,6s/^/#/g' test1.txt
cat -n test1.txt
# 取消文件中4到6行的注释
sed -i 's/^#//g' test1.txt
cat -n test1.txt
# 使用取得ens33网卡IP地址
ifconfig ens33 | sed -n '/netmask/p' | sed -e 's/^.*inet//' -e 's/netmask.*$//'
c. 删除_d
# 删除cat中的 第二行
cat -n test1.txt | sed '2d'
# 删除test1.txt源文件5到最后一行
sed -i '5,$d' test1.txt
# 根据匹配内容删除行
sed -i '/cat/d' test1.txt
d. 添加_a,i
- i(insert插入),在指定行前面插入一行
- a(append附加),在指定行后面添加一行
# 在第1行前面插入一行
sed -i '1i\hello first' test1.txt
cat -n test1.txt
# 在最后一行下面添加一行
sed -i '$a\hello last' test1.txt
cat -n test1.txt
# 在2-4行上面分别添加一行"=================="
sed -i '2,4i\==============' test1.txt
cat -n test1.txt
e. 替换整行_c
注意:"c"动作是进行整行替换的,如果仅仅想替换行中的部分数据,就要使用"s"动作
# 把所有=号开头的行修改为#
sed -i '/^=/c\############' test1.txt
cat -n test1.txt
# 永久关闭selinux
sed -i '/SELINUX=enforcing/c\SELINUX=disabled' /etc/selinux/config
sed -n '/SELINUX=/p' /etc/selinux/config
f. 读取_r
# 将test3文件中的内容 读取到test1文件的尾部
sed -i '$r./test3.txt' test1.txt
cat test1.txt
g. 写入_w
# 将test1中的内容 写入到 test3文件中
sed -i 'w./test3.txt' test1.txt
cat -n test3.txt
4、三剑客之 awk
awk命名: Aho、Weingberger 和 Kernighan三个人的姓的缩写。
awk是gawk的链接文件,awk是一种优良的文本处理工具,Linux及Unix环境中现有的功能最强大的数据处理引擎之一。相较于sed 常常作用于一整个行的处理, awk 则比较倾向于一行当中分成数个字段来处理。
awk 命令也是逐行扫描文件,寻找含有目标文本的行,如果匹配成功,则会在该行上执行用户想要的操作;反之,则不对行做任何处理。
语法:awk [options] '匹配规则{执行命令}' filename
-F 指定分隔符,awk使用空格或tab键作为默认分隔符
-v key=values在执行处理过程之前,设置一个变量
awk保留字 | BEGIN | 在awk程序一开始,尚未读取任何数据之前执行。BEGIN 后的动作只在程序开始时执行一次 |
awk保留字 | END | 在awk程序处理完所有数据,即将结束时执行。END 后的动作只在程序结束时执行一次 |
关系运算符 | > | 大于 |
< | 小于 | |
>= | 大于等于 | |
<= | 小于等于 | |
== | 等于。用于判断两个值是否相等 | |
!= | 不等于 | |
匹配表达式 | ~(匹配) | value ~ /regexp/ 如果value匹配/regexp/,则返回真 |
!~(不匹配) | value !~ /regexp/ 如果value不匹配/regexp/,则返回真 | |
正则表达式 | /正则表达式/ | 如果在“//”中可以写入字符,则也可以支持正则表达式 |
逻辑运算符 | && | 逻辑与 |
|| | 逻辑或 |
a. 完整格式
awk [options] 'BEGIN{ print "start" } 匹配规则{ commands } END{ print "end" }' filename
awk会自动给一行中的每个数据字段分配一个变量。
$0 | 代表整个文本行 |
$1 | 代表文本行中的第 1 个数据字段 |
$2 | 代表文本行中的第 2 个数据字段 |
$n | 代表文本行中的第 n 个数据字段 |
BEGIN 的执行时机是在 awk 程序一开始,尚未读取任何数据之前。一旦BEGIN后的动作执行一次,当awk开始从文件中读入数据时,BEGIN 的条件就不再成立,所以BEGIN定义的动作只能被执行一次。通过BEGIN开始块我们可以用来设置变量,设置标题。
END也是awk的保留字,不过刚好和 BEGIN 相反。END 是在 awk 程序处理完所有数据,即将结束时执行的。END 后的动作只在程序结束时执行一次
# 查看uid>1000的用户
awk -F ":" 'BEGIN{print "user\t\tbash"}$3>1000{print $1"\t\t"$7}' /etc/passwd
# 查看uid>1000的用户并在行尾输出结束标志
awk -F ":" 'BEGIN{print "user\t\tbash"} $3>1000{print $1"\t\t"$7} END{print "===================================\n the end\n==================================="}' /etc/passwd
# 打印/etc/passwd登陆shell为/bin/bash的用户
awk -F ":" '$7 ~ /bash$/{print $1"==>"$7}' /etc/passwd
# 使用awk过滤IP地址
ifconfig ens33 | awk '/netmask/{print $2}'
b. awk 自定义变量
# 统计用户数量,在执行过程之前设置变量
awk -F ":" -v column1='统计用户数' -v count=0 'BEGIN{print column1} {count++} END{print "user_count:",count}' /etc/passwd
c. awk 内置变量
内置变量 | 含义 |
$0 | 这个变量包含执行过程中当前行的文本内容 |
$n | 当前记录(当前行)的第n个字段,比如n为1表示第一个字段 |
FILENAME | 当前输入文件的名 |
FS | 字段分隔符(默认是任何空格) |
NF | 表示字段数,在执行过程中对应于当前的字段数 |
NR | 表示记录数,在执行过程中对应于当前的行号 |
FNR | 各文件分别计数的行号 |
# 打印/etc/passwd每行有多少列
awk -F ":" '/^root/{print NF}' /etc/passwd # 输出-> 7
# 过滤test1.txt带dog的行及行号
awk '/dog/{print NR,$0}' test1.txt
# awk多文件扫描,FNR每个文件单独统计行号
awk '{print NR,$0}' test1.txt test3.txt
# 统计当前内存的使用率
free -m | awk '/Mem/{printf "%.2f%%\n",(1-$4/$2)*100}'
d. awk if判断
单分支 | if (条件) print |
双分支 | if(条件){print}else{print} |
多分支 | if(条件){print}else if(条件){print}else{print} |
# 统计分区使用率,输出超过50%的分区信息
e. awk while循环
while(条件){语句}
# 计算每个学生的总分数
awk '$1 !~ /姓名/{total=0;i=5;while(i<=NF){total+=$i;i++};print $1," 总分数:",total}' score.txt
$1 !~ /姓名/
:过滤掉表格的头部- 定义变量:总分-total,内置变量NF代表有7部分,总分需要使用第5-7部分(各科成绩)
- 循环起始条件从5开始7结束,$i,如果i=5 则 $5是awk的第五列,也就是语文成绩,以此类推。
f. awk for循环
for(变量赋值;条件;迭代){命令}
# 遍历数组中的变量
awk 'BEGIN{name[0]="齐桓公";name[1]="楚庄王";name[2]="宋襄公";name[3]="晋文公";name[4]="秦穆公";for(i in name){print i,name[i]}}'
g. awk 数组
array[“index”]++ 每循环一次这个索引所对应的元素次数加1
# 统计Listen和Estableshed出现的次数
netstat -antu | awk '/tcp/{state[$NF]++} END{for(i in state){print i,state[i]}}'
-------------------------------
# 统计相同的网站出现的次数
awk -F "//" '{web[$NF]++} END{for(i in web){print web[i],i}}' ipcom.txt