全国协议5人面授小班,企业级独立开发考核,转业者的IT软件工程师基地 登录/注册 | 如何报名
当前位置: 服务端相关   >  Shell 正则表达式
admin · 更新于 2021-08-06

1. Shell 正则表达式概述

1.1 正则表达式是什么

正则表达式 (regular expression)是一些具体有特殊含义的符号,组合在一起的共同描述字符或字符串的方法,通俗来讲正则为描述同一类事物的规则,例如我们生活中描述可以飞行的是事物,则满足这条规则的可以是鸟,蝴蝶,也可以是飞机等。

在 Linux 系统中,正则表达式通常用来对字符或字符串来进行处理,它是用于描述字符排列或匹配模式的一种语言规则。

1.2 为什么要用正则表达式

我们知道正则表达式是一个描述字符排列或模式匹配的规则,我们可以利用它来制定自己的规则,获取到我们想要的结果等。在后续的 Shell 三剑客 grep/awk/sed Shell 的学习中,我们会结合正则表达式与这些命令进行结合使用,来实现更强大的文本处理功能。正则表达式是我们 Shell 学习的核心也是难点,在 Linux 中一切皆文件,多文件的处理可以覆盖我们日常工作的 90%,所有熟练掌握正则表达式显得尤为重要,在之后灵活配合其他命令可以非常方便的满足我们的日常处理需求。

2. Shell 正则表达操作

在学习正则表达式的操作之前我们需要了解下 POSIX 及正则表达式的分类。

2.1 预备知识

2.1.1 POSIX

POSIX 称为:Portable Operating System Interface(末尾增加 X 只是为了更流畅)的缩写,后来被 IEEE 采纳,由于在早期 Unix 系统时代各厂商发布不同版本的操作系统,各版本之间存在着产品的差异,之后 IEEE 发布了一套 Unix 和类 Unix 系统工作方式规范,至此各常见遵循此规范来达到软件兼容的效果。

2.1.2 正则表达式分类

正则表达式常见的有两种分类:

  • 基本正则表达式:Basic Regular Expression 又叫 Basic RegEx 简称 BREs,在基本正则表达式中作用的元字符为:^ 、$、 . 、[] 、* ;
  • 扩展正则表达式:Extended Regular Expression 又叫 Extended RegEx 简称 EREs,其为在基本正则表达式上新增了 () 、{ 、} 、?、 + 、等元字符元字符,使得正则表达式更加简洁易用。

2.1.3 字符

在学习正则表达式前需要先学习字符。

  • POSIX 字符

通用 POSIX 原字符如下:

[:alnum:] 字母数字[a-z A-Z 0-9][:alpha:]	字母[a-z A-Z][:blank:]	空格或制表键[:cntrl:] 任何控制字符[:digit:] 数字 [0-9][:graph:] 任何可视字符(无空格)[:lower:] 小写 [a-z][:print:] 非控制字符	
[:punct:] 标点字符[:space:] 空格[:upper:] 大写 [A-Z][:xdigit:] 十六进制数字 [0-9 a-f A-F]
代码块
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 特殊字符

在扩展正则表达式中加上 \ 则被认为其具有特殊含义:

\w 匹配任意数字和字母,等效[a-zA-Z0-9_]\W 和\w相反,等效[^a-zA-Z0-9_]\b 匹配字符串开始或结束,等效\<和\>\s 匹配任意的空白字符
\S 匹配非空白字符
代码块
  • 1
  • 2
  • 3
  • 4
  • 5

2.1.4 区别

  • 基本正则表达元字符:只有 ^$.*[];
  • 扩展正则表达式元字符:^$.[]*+(){}?|;
  • 扩展正则表达式对于 {m,n} 和 () 不需要再向基本正则表达式需要 \ 来转译。

2.2 正则表达式操作

在 Linux 中正则表达式用来处理文本,在此我们使用 grep 工具对正则表达式进行操作,grep 为文本过滤工具,在 grep 命令中默认使用的时候基本正则表达式,可以使用选项 -E 来开启扩展正则表达式,按照指定的正则表达式取出我们需求的内容。

2.2.1 字符匹配

在字符匹配前需要先学习。

  • .: 匹配任意单个字符,例如:
[root@master reg]# cat test.txt 
she
sh
s1e
[root@master reg]# grep "s.e" test.txt 
she
s1e
代码块
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

. 匹配必须为字母 s 与 e 中有任意单个字符。

  • [] : 匹配指定中括号范围内的任意单个字符,例如:
[root@master reg]# cat test.txt she
sh
s1e[root@master reg]# grep "s[a-z]e" test.txt she[root@master reg]# grep "s[1-9]e" test.txt    s1e[root@master reg]# grep "s[[:alnum:]]e" test.txt      // 匹配字符或数字  she
s1e[root@master reg]# grep "s[[:alpha:]]e" test.txt    she[root@master reg]# grep "s[[:digit:]]e" test.txt      s1e
代码块
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

中括号内可以利用元字符来表示。

    [root@master reg]# cat test.txt she
    sh
    s1e[root@master reg]# grep "s[^[:digit:]]e" test.txt    she[root@master reg]# grep "s[^a-z]e" test.txt          s1e
    代码块
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    如上,匹配的元字符取反,也就是不包含匹配的内容。

    2.2.2 次数匹配

    次数匹配用在指定的字符后面,表示指定匹配到前面的字符出现多少次。

    • *: 匹配前面的字符任意次(0 次或无数次),例如:
    [root@master reg]# cat test2.txt ssssh
    sheee
    hell[root@master reg]# grep "s*" test2.txt ssssh
    sheee
    hell	
    
    代码块
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    如上匹配字符 s,0 次或多次。

    • \?: 匹配前面的字符 0 次或 1 次,例如:
    [root@master reg]# cat test2.txt ssssh
    sheee
    hell[root@master reg]# grep "s\?h" test2.txt ssssh						# 匹配最后的shsheee						# 匹配shhell						# 匹配h[root@master reg]# grep -E "s?h" test2.txt  ssssh
    sheee
    hell
    代码块
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    如上匹配 s 可以存在 0 次,或者存在 1 次之后需要有 h 字符,注意利用选项 -E 开启扩展正则表达式,相较于基本正则表达式不需要 \

    • +: 匹配前面的字符至少 1 次,例如:
    [root@master reg]# cat test2.txt ssssh
    sheee
    hell[root@master reg]# grep "s\+h" test2.txt  ssssh						# 匹配sssshsheee						# 匹配sh[root@master reg]# grep -E "s+h" test2.txtssssh
    sheee
    代码块
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    如上匹配 s 至少存在 1 次或无数次。

    • \{m\,}: 匹配前面的字符至少 m 次(默认工作在贪婪模式下,? 取消贪婪模式),例如:
    [root@master reg]# cat test2.txt ssssh
    sheee
    hell[root@master reg]# grep "s\{1,\}" test2.txt ssssh
    sheee[root@master reg]# grep -E "s{1,}" test2.txt   ssssh
    sheee[root@master reg]# grep "s\{2,\}" test2.txt  ssssh[root@master reg]# grep -E "s{2,}" test2.txt   ssssh
    代码块
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    匹配字符 s,最少 1 次。

    • \{,n}: 匹配前面的字符最多 n 次(默认工作在贪婪模式下,? 取消贪婪模式),例如:
    [root@master reg]# cat test2.txt ssssh
    sheee
    hell[root@master reg]# grep "s\{,2\}" test2.txt     ssssh
    sheee
    hell[root@master reg]# grep -E "s{,2}" test2.txt  ssssh
    sheee
    hell
    代码块
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    匹配字符 s,最多 2 次。

    • \{m,n\}: 匹配前面的字符至少 m 次,至多 n 次,例如:
    [root@master reg]# cat test2.txt ssssh
    sheee
    hell[root@master reg]# grep "s\{1,2\}" test2.txt    ssssh
    sheee[root@master reg]# grep -E "s{1,2}" test2.txt ssssh
    sheee
    代码块
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    匹配字符 s,1-2 次之间。

    • .*: 匹配任意字符任意次数。

    2.2.3 位置锚定

    • ^: 行首锚定,用于模式最左边,例如:
    [root@master reg]# cat test2.txt ssssh
    sheee
    hell[root@master reg]# grep "^s" test2.txt ssssh
    sheee
    代码块
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    匹配以 s 开头的行。

    • $: 行尾锚定,用于模式最右边,例如:
    [root@master reg]# cat test2.txt ssssh
    sheee
    hell[root@master reg]# grep "h$" test2.txt ssssh
    代码块
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    匹配以 h 结尾的行。

    • \< 或 \b: 锚定词首,用于单词模式左侧,例如:
    [root@master reg]# cat test2.txt go root userroot:shell;gousers
    hellorootgouser[root@master reg]# grep "\<ro" test2.txt go root userroot:shell;gousers[root@master reg]# grep "\bro" test2.txt         go root userroot:shell;gousers
    代码块
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    可以看到此刻匹配是以单词模式,没有匹配 helloroot。

    • \> 或 \b: 锚定词尾,用于单词模式右侧,例如:
    [root@master reg]# grep "gouser\b" test2.txt hellorootgouser[root@master reg]# grep "gouser\>" test2.txt  hellorootgouser
    代码块
    • 1
    • 2
    • 3
    • 4

    2.2.4 分组引用

    • () 分组:将一个或多个字符当成一个整体来进行后续处理;
    • 1…数字引用:从左侧起,引用第一个左括号以及与之匹配右括号之间的模式所匹配到的字符,后向引用,例如:
    grep -E "(root).*\1" /etc/passwd
    代码块
    • 1

    利用 () 将 root 引用起来,后面利用数字 1 引用。

    3. 实例

    1.显示/etc/init.d/functions文件中以大小s开头的行grep '^[Pp]' /etc/init.d/functions
    
    2.显示/etc/passwd文件中以/bin/bash结尾的行grep "/bin/bash$" /etc/passwd
    
    3.显示/etc/passwd文件中ID号最大用户的用户名sort -t: -k3 -n /etc/passwd |tail -1 |cut -d: -f1
    
    4.如果root用户存在,显示其默认的shell程序id root && grep '^\<root\>' /etc/passwd |awk -F: '{print $NF}'5.找出/etc/passwd中的两位或三位数grep -o "[0-9]\{2,3\}" /etc/passwd
    
    6.显示/etc/rc.d/rc.sysinit文件中,至少以一个空白字符开头的且后面存非空白字符的行:grep '^[[:space:]]\+[^[:space:]]' /etc/rc.d/rc.sysinit
    
    
    7.找出"netstat -tan"命令的结果以"LISTEN"后跟0,1或多个空白字符结尾的行netstat -tan|grep 'LISTEN[[:space:]]*$'8.如果root用户登录了系统,就显示root用户在线,否则说明未登录
    w |grep '^\<root\>'>/dev/null && echo "root在线"|| echo "root未登录"9.找出/etc/rc.d/init.d/functions文件中某单词后面跟一对小括号的行grep '[[:alpha:]]*()' /etc/rc.d/init.d/functions
    
    10.使用echo输出一个路径,使用egrep取出基名echo /tmp/tmp1/vmstat.8.gz |grep -E  -o '[^/]+/?$'|cut -d/ -f1
    
    11.匹配PPID开头,行中又再次出现PPID的内容。/etc/init.d/functionsgrep -E "(PPID).*\1" /etc/init.d/functions
    
    12.利用awk找出/etc/ssh/sshd_config内出过空行与以#开头的行grep -v -E '^#|^$' /etc/ssh/sshd_config
    代码块
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    4. 注意事项

    • 正则表达式中基本正则表达式与扩展正则表达式配合其它操作,能够千变万化,非常灵活,根据不同场景可以进行正向匹配和反向匹配;
    • 正则表达式配合命令通常为三剑客 grep/awk/sed 等,后期灵活进行组合达到事半功倍的效果;
    • 我们在文本模式匹配的时候可以考虑使用扩展的正则表达式,从而避免使用过多的转义字符。

    5 小结

    正则表达式可谓 Shell 中的精华,其实在其他语言中也很通用,需要进行勤加练习才能达到熟练掌握,注意区分基本正则表达式与扩展正则表达式的语法区别,配合其他工具灵活运用。在不同场景可以利用命令选项配合使用,在后期 Shell 脚本三剑客中也会频繁出现正则表达式,攻克正则表达式这个难关,Shell 脚本编程就已经事半功倍。


    为什么选择汉码未来