正则表达式
正则表达式是一种匹配模式,通过正则表达式,用户可以精准匹配自己想要的内容。
正则表达式基本是由2个部分组成,分别是一般字符及特殊字符。一般字符是指没有任何意义的字符,也就是说字符没有特殊含义,比如,a,b,c,d等字符;特殊字符是指拥有特殊含义,并且在一定情况下可以转为一般字符那种,也被称为元字符。
正则表达式可以分为基础正则表达式(BRE)及扩展正则表达式(ERE)。
- 基础正则表达式
- 常见的特殊字符:
BRE/ERE | 特别字符 | 描述(默认以BRE功能为主) |
2 | $ | 匹配输入字符串的结尾位置。要匹配$ 字符本身,ERE可以放在任意位置 |
BRE | ( ) | 标记一个子表达式的开始和结束位置。要匹配这些字符,请使用\( 和\) |
2 | * | 匹配前面的子表达式零次或多次。要匹配* 字符,请使用\*(位于第一个字符就没有任何意义,) |
2 | + | 匹配前面的子表达式一次或多次。要匹配+ 字符,请使用\+ |
2 | . | 匹配除换行符\n 之外的任何单字符。要匹配. ,请使用\. ;nul除外 |
[ | 标记一个中括号表达式的开始。要匹配[,请使用\[ | |
2 | ? | 匹配前面的子表达式零次或一次,或指明一个非贪婪限定符。要匹配? 字符,请使用\? |
\ | 将下一个字符标记为或特殊字符、或原义字符、或向后引用、或八进制转义符。例如,'n' 匹配字符'n'。'\n' 匹配换行符。序列'\\' 匹配"\",而'\(' 则匹配"(" | |
2 | ^ | 匹配输入字符串的开始位置,除非在方括号表达式中使用,此时它表示不接受该字符集合。要匹配^ 字符本身,请使用\^;ERE置于任何位置都有意义 |
{ | 标记限定符表达式的开始。要匹配{,请使用\{ | |
2 | [...] | 方括号表达式,匹配方括号内的任一字符, [a-c],连字符,指的是范围,[^a],^放在方括号里面,则是取反的意思,不匹配里面任一字符 |
BRE | \{n,m\} | 区间表达式,匹配它前面单个字符重现的次数。\{n\}指重现n次, \{n,\}至少重现n次,\{n,m\}重现n到m次 ,n与m的值介于0到RE_DUP_MAX之间,centos7中,最大值是32767 # getconf RE_DUP_MAX |
BRE | \( \) | \( "保留空间"\) 主要是存储子表达式,最多可以存储9个 |
ERE | {n,m} | 是属于ERE独有的区间表达式 |
2 | | | 指明两项之间的一个选择。要匹配|,请使用\| 如:Y | y |
定位符 | ||
^ | 匹配输入字符串开始的位置 | |
$ | 匹配输入字符串结尾的位置 | |
非打印字符 | ||
BRE | \s | 空格 \s*0个或者多个空格 |
\n | 匹配一个换行符 | |
\r | 匹配一个回车符 | |
\t | 匹配一个制表符 |
- 常见表达式:
# cat wc.sh
matchtouchmkdirnginxmysql
# cat wc.sh | egrep "touch" #只打印"touch"几个字符,跟常规grep匹配差不多
matchtouchmkdirnginxmysql
# cat wc.sh | egrep "^ma" #匹配以"ma"字符开头的行
matchtouchmkdirnginxmysql
# cat wc.sh | egrep "mysql$" #匹配以"mysql"字符结尾的字符,跟命令行中的$差不多
matchtouchmkdirnginxmysql
# cat wc.sh | egrep "^matchtouchmkdirnginxmysql$" #匹配完整的内容,也就是这一行只能且只有这些内容
matchtouchmkdirnginxmysql
# cat wc.sh | egrep "[nN]ginx" #[ ]里面是匹配单个字符,只要满足这个要求,无论是啥字符都行,同时在很多命令中,[ ]里面的内容是可选的
matchtouchmkdirnginxmysql
# cat wc.sh | egrep "c.m" #匹配c-m之间的单个字符
matchtouchmkdirnginxmysql
# cat wc.sh | egrep "c.*m" #匹配c-m之间的0个或者多个字符
matchtouchmkdirnginxmysql
# cat wc.sh | egrep "c.+m" # + 是匹配前面子表达式的1个或者多个字符
matchtouchmkdirnginxmysql
# cat wc.sh | egrep "c.?m" # ? 匹配前面0个或者1个字符
matchtouchmkdirnginxmysql
- 常见特殊字符集(在shell里面实践了一下好像跑不了,据说是php的正则表达一种):
-
基础正则表达式
支持单个字符匹配及多个字符匹配 (用到的字符就是BRE里面的所有标准字符及特殊字符)
[ : : ] 字符集,可以把多个字符组合起来成为一个完整的字符串
[ . . ] 排序元素 主要是进行排序
[ = ] 等价字符集,也就2边的字符都是相等的
这几种字符集方式的实现都需要[ ] 来帮忙实现。
在 [ ] 中所有字符都会失去其特殊含义。[ * \ . ] 就是字面上的意思,正确写法:[ ] * \ .]
-
分组
分组简单来说是把一串字符串,通过 \( \) 方式形成一个子表达式(单个字符),供后面的正则去匹配,并且最多存储9个子表达式,用数字1-9表示。1个 \( \) 就是一个分组,就是一个整体。
$ cat ac.txt
hello
helloo
hellooo
hellohel
hellohell
hellohello
$ cat ac.txt | grep "hello\{3\}" #\{n\}在BRE里面是匹配前面单个字符出现的次数
hellooo
$ cat ac.txt | grep "\(hello\)\{2\}"
hellohello
#在上面的表里面,有个 \( \) 特殊字符,这里面主要是存放子表达式的,也就是把内容整合为一个整体,
供后面的正则去匹配,简单来说就是把多个字符组合一起,成为单个字符
分组还可以嵌套的:
$ cat ac.txt | tail -3
hellohello
queuequeue
queue
$ cat ac.txt | grep '\(q\(ue\)\{2\}\)\{2\}'
queuequeue
#这就是个简单的嵌套,也就是这样:\( \( \) \)优先运行里面的\( \) 然后在运行外面最大的\( \)
上面例子解析:
首先运行 q\(ue\)\{2\} 结果是queue,也就是把ue作为一个整体匹配了出现的次数也就是2次
然后运行最外面的 \(queue\)\{2\},结果就是queuequeue,在进行子表达式嵌套的时候会把最里面子
表达的结果给外面的子表达式作为结果输入,跟Linux中的|一个意思。
grep '\(ue\)\{2\}\)' 与 grep '\(ue\{2\}\)' 是有区别的,前者是把ue作为一个字符,后者是把e作为字符
$ cat ac.txt | grep '\(ue\)\{2\}'
queue
$ cat ac.txt | grep '\(ue\{2\}\)'
quee
- 后向引用
$ cat 7.txt
tzc word yunying
lij word lij
haibing word yunying
需求1:打印tzc那一行
$ cat 7.txt | grep 't.\{2\}\s*word y.\{3,8\}' # . 是表示除\n之外的任意单个字符,这个表示只匹配tzc了
tzc word yunying
需求2:匹配word字段周边都相同的行
$ cat 7.txt | grep '\(l.\{2\}\)\s*word \1' #出现全新玩法 \1
lij word lij
注:\1是什么了?这个就是后向引用,也就是\1是引用第一个分组的结果,\( \)这就是分组括号,里面的结果就是分组结果,\1就是匹配第一个分组的结果。
严重强调:\1 \2等等只能匹配\( \)的结果
$ cat test.txt
abccccabeeee word abccccabeeee
$ cat test.txt | grep '\(\(abcc.\{2\}\)\ab.\{4\}\) word \2'
abccccabeeee word abccccabeeee
$ cat test.txt | grep '\(\(abcc.\{2\}\)\ab.\{4\}\) word \1'
abccccabeeee word abccccabeeee
根据上面结果显示,后向引用顺序是由左侧分组符号进行排序的也就是 ((( 分别是\1 \2 \3
后向引用需要以分组为前提的不然就引用不了,后向引用只能引用分组符号 \( \)的匹配结果
\1 表示引用第一个分组括号里面的正则匹配结果,\2 \3依次类推
-
优先级
BRE运算符优先级,由高至低
运算符 | 表示含义 |
[..] [==] [::] | 用于字符排序的方括号符号 |
\metacharacter | 转义的 meta 字符 |
[ ] | 方括号表达式 |
\( \) \digit | 子表达式与后向引用 |
*\{ \} | 前置单个字符重现的正则表达式 |
无符号(no symbol) | 连续 |
^ $ | 锚点 |
- 扩展正则表达式
ERE是扩展正则表达式,扩展正则表达式在标准正则表达式上新增一些功能,能满足大家的工作需求。
单个字符匹配跟跟BRE方法是差不多的,但是ERE中的awk里面 \ 有特殊的含义。
- 反向应用:在通过sed进行扩展正则的时候一样可以使用
- 字符匹配:ERE在字符匹配方面,与BRE有明显的不同,其中*的功能是一样的。
- 区间表达式:ERE也存在区间表达式,但是不在是 \{ \},而是以 { } 这种形式展现,且不需要前置 \ 字符。
- 分组:ERE进行分组的话是 ( ),不需要在加 \ 字符
- 交替: | 跟BRE方式一样,二选一那种 (read|write) 还有是添加锚点符那种 ^action|active$ 匹配字符串开始处是否有action或者结尾处是否有active ^(ac|bc)$匹配当前内容里面是否有ac或者bc字符串
-
优先级:跟BRE是一样的由高到低
-
额外正则运算表达符
运算符 | 表示含义 |
\w | 匹配任意单词(可以是单个字符,字符串等等) ac bh ki js n r |
\W | 匹配任意符号(可以是单个,也可以是多个) ? \ + - == [ ] { } -- () |
\< \> | \<ac 匹配以ac开头的字符 df\> 匹配以df结尾的字符 \<qwe\> 那就只匹配qwe字符 |
\b | 单词边界,就是单词和符号之间的边界,单词就是\w匹配的内容,符号是\W匹配的内容,也就是匹配字跟空格之间的一个位置 # cat wc.sh # cat wc.sh | egrep 's\b' # cat wc.sh | egrep '\bma' # cat wc.sh | egrep '\bac\b' 总结:通过\b可以匹配开头处的内容及结尾处的内容,至于中间的内容需要\B来匹配 注:更深层次的理解,等我理解完毕再来补充 |
\B | 非单词边界,是单词与单词,符号与符号之间的边界 # cat wc.sh | egrep '\B1223\B' 总结:简单来说就是匹配某个内容是否存在 注:更深层次的理解,等我理解完毕再来补充 |
非常好的正则使用例子:
root@test:/opt/scripts 12:47:52
$ cat hl-ip.txt.bak | sort | awk -F'/' '{print $3,$0}' | sed -r 's/.*\.(\w+).(com|net) /\1.\2 /'| sort -k 1 | awk '{print $2}'
http://10.pic.123456.com/2017-08/mB8CBuXo8u87MFthMmWSEJymrQHS2G5hkgWSAR2q.jpeg
http://1.pic.234567.com/2017-07/FLeGYVYY5EPakINvcORvJvxoR9z19HG2McSuAeww.jpeg
位置参数
位置参数分别是$0 $1 $2.....$n
# sh system.sh 1 2 3 4
根据上面那个命令展示:$0 就是命令本身 1 是$1 2 是$2 3是$3 依次类推
程序跟踪测试
在Linux命令行:sh -x 叫脚本 #一般都是使用这种方式
在shell脚本里面:以set -x开启跟踪模式 以set -x关闭跟踪模式,例如:
#!/bin/bash
who | wc -l >/dev/null
set -x
if [ $? = 0 ]
then
echo "zhixingchenggong"
else
echo "zhixingfailed"
fi
set -x
echo abc
root@test:/opt/scripts/my.scripts 18:52:22
# sh wc.sh
+ '[' 0 = 0 ']'
+ echo zhixingchenggong
zhixingchenggong
+ set -x
+ echo abc
abc
特殊文件
/dev/null 和 /dev/tty
/dev/null 熟称黑洞,将标准输出输出到这个文件后,输出结果会被系统给丢掉,不占用系统任何空间,非常的实用。
/dev/tty 当程序打开此文件时,shell会自动将它重定向到一个实体终端(不怎么用过)
文本输出
echo
printf
printf是模仿C程序库的printf()库里程序,但是功能相当于与echo而言,更复杂,更灵活。printf不支持自动换行,echo支持自动换行
root@test:/opt/scripts/my.scripts 17:45:12
# printf "hello word"
hello wordroot@test:/opt/scripts/my.scripts 17:45:37
# echo "hello word"
hello word
root@test:/opt/scripts/my.scripts 17:47:38
# printf "hello word\n"
hello word
注:根据上图所示,printf不支持换行,需要加\n才行
用法:printf FORMAT [ARGUMENT]...