• 正则表达式与 grep

    • 注意事项
    • 基础正则与实战
    • 扩展正则与实战
  • 三剑客 sed

1. 正则

📢 注意事项

  • 英文字符

1.1 符号概述

正则表达式
基础正则 ^ $ . * .* [] [^]
扩展正则 `
其他类型正则

1.2 基础正则

三剑客命令默认支持的正则

^ 以…开头的行

image-20240717224125380

$ 以…结尾的行

  • 以数字 6 结尾

    image-20240717224309647

  • 以字母 m 结尾

    image-20240717224401323

  • cat -A 显式出隐藏的字符

    image-20240717224755968

^$ 空行,这行中没有任何字符

image-20240717224938456

排除空行

image-20240717225017418

用于排除文件中的空行,排除空行和带井号的行

. 任意一个字符

  • oldb任意一个字符y

    image-20240717225350065

    了解:. 过滤的时候会排除空行,. 不会匹配空行

\ 转义字符

找出以 . 结尾的

image-20240718090235105

* 匹配多次

前一个字符连续出现 0 次或 0 次以上

image-20240718104453599

.* 所有

点表示任意字符,星号表示多个,所以.*表示所有字符

image-20240718104651438

贪婪:正则表示连续出现的时候,表示所有的时候,体现出贪婪性

  • 匹配开头一直到 o 的内容

    image-20240718105154102

[] 匹配任意一个字符

[abc] 表示匹配任意一个字符,a 或 b 或 c,中括号相当于一个字符

image-20240718105413725

  • 匹配数字

    1
    
    grep '[0-9]' re.txt
    
  • 匹配小写字母

    1
    
    grep '[a-z]' re.txt
    
  • 匹配大写字母

    1
    
    grep '[A-Z]' re.txt
    
  • 匹配大小写字母

    1
    
    grep '[a-zA-Z]' re.txt
    
  • 匹配大小写字母+数字

    1
    2
    
    grep '[a-zA-Z0-9]' re.txt 
    grep '[0-Z]' re.txt
    
  • 匹配出以字母 m 或 n 开头的行

    image-20240718113603563

  • 匹配出以 . 或空格或!结尾的行

    image-20240718113709442

[ ] 会自动去掉符号的特殊含义

[^] 取反

[^abc] 表示匹配任意 1 个字符,排除 abc,中括号相当于一个字符

image-20240718114011872

小结

基础正则 含义
^ 以xxx 开头的行
$ 以…结尾的行
^$ 空行
. 任意一个字符
\ 转义字符
* 前一个字符出现0 次或多次
.* 所有
[ ] 匹配括号中的一个字符
[^] [^abc] 匹配除了 abc 之外的内容

1.3 扩展正则

  • egrep 或 grep -E
  • sed使用 sed -r 支持扩展正则
  • awk 默认支持扩展正则

+ 前一个字符连续出现 1 次或以上

+ 大部分配合[ ] 使用

  • 取出连续出现的 0

    image-20240718141059754

  • 取出连续出现的数字

    image-20240718141236332

  • 取出连续出现的字母

    image-20240718141314633

    image-20240718141441396

| 或者

  • 文件中包含 oldboy 或linux 的

    image-20240718141545807

  • 排除/ect/ssh/sshd_config 中的空号或者注释行,输出显式行号

    image-20240718142114106

() 表示一个整体,用于后向引用(反向引用 sed)

image-20240718142447147

image-20240718142708279

{} 大括号

a{n,m} 前一个字符连续出现至少 n 次,最多 m 次

格式 解释
a{n,m} 前一个字符连续出现至少 n 次,最多 m 次 表示连续出现的范围
a{n} 前一个字符连续出现n 次 匹配固定的次数
a{n,} 前一个字符至少连续出现 n 次
a{,m} 前一个字符连续出现,最多 m 次

image-20240718143917915

匹配身份证的正则:

分析:前 17 为为数字,后一位为数字或为 X

image-20240718144228285

? 前一个字符出现 0 次或 1 次

image-20240718144715051

小结

扩展正则 含义
+ 前一个字符连续出现 1 次或多次
| 或者
( ) 🅰️ 表示整体,🅱️ 后向引用或反向引用(sed)
{ } a{n,m} 前一个字符连续出现至少 n 次,最多 m 次
? 前一个字符出现 0 次或 1 次

1.4 perl 语言正则

符号 含义
\d [0-9]
\s 匹配空字符 空格、tab 等
\w [0-9a-zA-Z_]
\D [^0-9] 排除数字
\S 非空字符
\W 排除数字、字母和_

image-20240718145439957

1.5 零碎的正则

image-20240718150112675

2. sed

2.1 概述

  • 取行,过滤,替换修改文件内容
  • 后向引用

2.2 格式

sed 选项 ‘条件动作’ 文件
‘找谁干啥’
选项 说明
-n 取消默认输出
-r sed支持扩展正则
-i 修改文件内容,这个选项放在最后
-i.bak 先备份在修改,这个选项放最后

2.3 如何运行

  • 模式空间:容纳当前行的缓冲区,即通过模式匹配到的行被读入该空间中
  • 保持空间:一个辅助缓冲区,可以和模式空间进行交互(通过h,H,g,G),但命令不能直接作用于该空间,在进行数据处理时作为“暂存区域”

原理概念

执行步骤:

1)读入一行数据到模式空间

2)在模式空间执行sed命令

3)将更新/修改后的内容输出

4)清空模式空间,并重复第一步,直到文件结束

执行流程

2.4 sed 增删改查之查找

  • 类似grep 命令的过滤,比 grep 强在可以指定行号
  • 类似于 grep 命令的功能,模糊查询(通过正则)

image-20240718192412277

案例 1: 取出文件第三行

image-20240718155037784

案例 2:取出/etc/passwd的第2行到第5行

image-20240718155205169

案例 3:过滤出/etc/passwd中包含root 的行

image-20240718155509804

sed 进行过滤的时候需要使用// 并且里面支持基础正则

如果需要使用扩展正则则需要使用 sed -r 选项

案例:获取范围内的日志

1
2
3
4
5
6
7
8
cat >sed.txt<<EOF
101,oldboy,CEO
102,bigbao,CTO
103,李导996,COO
104,yy,CFO
105,feixue,CIO
110,lidao,COCO
EOF

image-20240718160021447

案例:取出 access.log 过滤出 11:05 到 11:06分的日志

1
sed -n '/11:05:00/, /11:06:00/p' access.log

案例:只显示第 2 行和第 5 行

image-20240718162902533

案例:表示有规律的查找

image-20240718163146125

2.5 sed 增删改查之修改

  • sed 的修改叫做替换
  • sed 默认不修改,加上 -i 选项能修改
  1. 案例:把 sed.txt 文件中 lidao 替换为 oldboy

    1
    
    sed 's#lidao#oldboy#g' sed.txt
    
    1
    2
    3
    4
    
    sed 's###g' sed.txt
    sed 's#找谁#替换成什么#g' sed.txt
    s substitute 替换
    g global 全局替换 这一行中把所有匹配到的内容全都进行替换,否则只替换每一行第一个匹配的内容
    
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    
    ➜  ~ sed 's#[0-9]##g' sed.txt    # 将数字替换为空
    ,oldboy,CEO
    ,bigbao,CTO
    ,李导,COO
    ,yy,CFO
    ,feixue,CIO
    ,lidao,COCO
    ➜  ~ sed 's#[0-9]##' sed.txt
    01,oldboy,CEO
    02,bigbao,CTO
    03,李导996,COO
    04,yy,CFO
    05,feixue,CIO
    10,lidao,COCO
    

    image-20240718164343092

    sed 命令替换的时候 s###g

    也可以写为 s@@@g s///g

  • 修改文件内容之前进行备份,然后修改文件内容

    1
    
    sed -i.bak 's#bigbao#oldbao#g' sed.txt
    

2.6 增删改查之替换

123456 加上<> 变成<123456>

1
2
➜  ~ echo 123456 | sed -r 's#(.*)#<\1>#g'
<123456>

\1 表示前面(.*)中匹配到的内容 ()小括号表示分组 \1 表示取出第一组的内容, -r 表示用到扩展正则

  • 给每个数字都加上<>

    1
    2
    3
    4
    
    ➜  ~ echo 123456 | sed -r 's#([0-9])#<\1>#g'
    <1><2><3><4><5><6>
    ➜  ~ echo 123456 | sed -r 's#(.)#<\1>#g'
    <1><2><3><4><5><6>
    

后向引用格式

应用说明:

sed 命令用于处理列的方式。

使用格式:

使用替换的形式 s###g

前两个井号之间通过正则加小括号,进行分组,

后两个井号之间通过 \数字 获取前面分组的内容,

整体是后面调用前面分组的内容,所以称之为反向引用(后向引用)

案例

  1. 调换 /etc/passwd 第一列和最后一列的内容

    1
    2
    3
    4
    5
    
    root:x:0:0:root:/root:/bin/zsh
    bin:x:1:1:bin:/bin:/sbin/nologin
    daemon:x:2:2:daemon:/sbin:/sbin/nologin
    adm:x:3:4:adm:/var/adm:/sbin/nologin
    lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
    
    1
    
    sed -r 's#(^.*)(:x.*:)(.*$)#\3\2\1#g' passwd
    
  2. 取出网卡 ip 地址

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    
    ➜  ~ ip a s eth0
    2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
        link/ether 00:16:3e:2f:45:fa brd ff:ff:ff:ff:ff:ff
        inet 172.18.243.120/20 brd 172.18.255.255 scope global dynamic eth0
           valid_lft 313947416sec preferred_lft 313947416sec
        inet6 fe80::216:3eff:fe2f:45fa/64 scope link
           valid_lft forever preferred_lft forever
    ➜  ~ ip a s eth0 |sed -n '3p'
        inet 172.18.243.120/20 brd 172.18.255.255 scope global dynamic eth0
    ➜  ~ ip a s eth0 |sed -n '3p' | sed -r 's#^.*inet (.*) brd.*$#\1#g'
    172.18.243.120/20
    

    进阶:

    1
    2
    3
    4
    5
    
     sed -n '3p'
     sed -r 's#^.*inet (.*) brd.*$#\1#g'
    
     ---
     sed -nr '3 s#^.*inet (.*) brd.*$#\1#g p'
    
  3. 取出 stat /etc/hosts 中的 0644 或 644

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    
    ➜  ~ stat /etc/hosts
      文件:"/etc/hosts"
      大小:213       	块:8          IO 块:4096   普通文件
    设备:fd01h/64769d	Inode:262194      硬链接:1
    权限:(0644/-rw-r--r--)  Uid:(    0/    root)   Gid:(    0/    root)
    最近访问:2024-07-20 10:28:17.784713652 +0800
    最近更改:2024-07-04 10:26:00.231943650 +0800
    最近改动:2024-07-04 10:26:00.231943650 +0800
    创建时间:-
    
    ➜  ~ stat /etc/hosts | sed -nr '4 s#^.*\((.*)/-rw.*$#\1#g p'
    0644
    

2.7 增删改查之删除

  • d sed 命令删除功能按照 行 为单位进行
  • 如果仅仅删除某一行的一些字符推荐使用 ’s###g'
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
➜  ~ cat sed.txt
101,oldboy,CEO
102,oldbao,CTO
103,李导996,COO
104,yy,CFO
105,feixue,CIO
110,oldboy,COCO
➜  ~ sed '3d' sed.txt  # 删除第三行
101,oldboy,CEO
102,oldbao,CTO
104,yy,CFO
105,feixue,CIO
110,oldboy,COCO

排除/删除 文件中的空行和带注释的行

1
sed -r '/^$|#/d' /etc/ssh/sshd_config

2.8 增删改查之增加

  • cai

    • a append 在指定行后追加

       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      15
      
      ➜  ~ cat sed.txt
      101,oldboy,CEO
      102,oldbao,CTO
      103,李导996,COO
      104,yy,CFO
      105,feixue,CIO
      110,oldboy,COCO
      ➜  ~ sed '3a 1024,aszhc,ufc' sed.txt
      101,oldboy,CEO
      102,oldbao,CTO
      103,李导996,COO
      1024,aszhc,ufc
      104,yy,CFO
      105,feixue,CIO
      110,oldboy,COCO
      
    • i insert 在指定行上插入

    • c replace 替换指定行的内容

3. awk

单行脚本

  • 取行
  • 取列
  • 混合取行列
  • 判断与运算
  • 数组
四剑客 特点 擅长
find 查找文件 查找文件
grep/egrep 过滤 过滤速度最快
sed 过滤、取行、替换、删除 替换,修改,取行
awk 过滤、取行、取列、统计计算、判断、循环… 取行、取列、统计计算
  • awk是一个语言,叫做单行脚本

2.1 概述

1. 格式

1
2
3
4
5
6
取出/etc/passwd中第一行的第一列、第三列和最后一列
awk -F 'NR==1{print $1,$3,$NF}' /etc/passwd

awk 选项 '条件{动作}'  /etc/passwd
		条件:找谁
		动作:做什么

2. 执行流程

2.2 取行

  • 取出/ect/passwd 的第一行

    1
    
    awk 'NR==1{print $0}' /etc/passwd
    

NR:Number of Record 记录,行号

== 表示等于

{print $0} 输出正好内容, $0 表示当前行的内容

仅取行,可以简写为:

1
awk 'NR==1' /etc/passwd
  • 取出第 2 行到第 5 行的内容

    1
    2
    3
    4
    5
    
    ➜  ~ awk 'NR>=2 && NR<=5' passwd
    bin:x:1:1:bin:/bin:/sbin/nologin
    daemon:x:2:2:daemon:/sbin:/sbin/nologin
    adm:x:3:4:adm:/var/adm:/sbin/nologin
    lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
    

    >= 表示大于等于

    && 表示并

    || 表示或

awk常用的运算符 说明
==、 != 等于
>、<、>=、<=
&&
||
  • 过滤出/ect/passwd文件中包含 root 或 nobody的行

    1
    2
    3
    4
    5
    
    ➜  ~ awk '/root|nobody/' /etc/passwd
    root:x:0:0:root:/root:/bin/zsh
    operator:x:11:0:operator:/root:/sbin/nologin
    nobody:x:99:99:Nobody:/:/sbin/nologin
    nfsnobody:x:65534:65534:Anonymous NFS User:/var/lib/nfs:/sbin/nologin
    
  • 从包含 root 的行包含 nobody 的行

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    
    ➜  ~ awk '/root/,/nobody/' /etc/passwd
    root:x:0:0:root:/root:/bin/zsh
    bin:x:1:1:bin:/bin:/sbin/nologin
    daemon:x:2:2:daemon:/sbin:/sbin/nologin
    adm:x:3:4:adm:/var/adm:/sbin/nologin
    lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
    sync:x:5:0:sync:/sbin:/bin/sync
    shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
    halt:x:7:0:halt:/sbin:/sbin/halt
    mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
    operator:x:11:0:operator:/root:/sbin/nologin
    games:x:12:100:games:/usr/games:/sbin/nologin
    ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
    nobody:x:99:99:Nobody:/:/sbin/nologin
    
  • 小结

    • awk + NR 取出指定的行,指定范围的行
    • awk + // 过滤
    • awk + 其他变量功能用于精确过滤

2.3 取列

  • 使用 awk 取出 ls -lh 的大小列和最后一列

    1
    2
    3
    4
    
    ➜  ~ ls -lh /etc/hosts |awk '{print $5,$9}'
    213 /etc/hosts
    ➜  ~ ls -lh /etc/hosts |awk '{print $5,$NF}'
    213 /etc/hosts
    

awk中, $数字 表示列,$1 表示第一列,$0表示当前这行

$NF最后一列

NF Number of Field 每行有多少列

$(NF-1) 倒数第二列,用于行数太多,或者正序数无规律的情况

1
ls -lh /etc/hosts |awk '{print $(NF-1)}' # 倒数第二列
  • 取出/etc/passwd中第一列,第 3 列和最后一列

    awk 取列的时候,默认是通过空白字符进行分割的

    空白字符:空格,连续空格,tab 键

    但是一些时候默认分隔符不够了,需要使用手动指定分隔符,通过-F 指定

    选择分隔符建议:看你目标两边是啥

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    
    ➜  ~ awk -F':' '{print $1,$3,$NF}' passwd
    root 0 /bin/zsh
    bin 1 /sbin/nologin
    daemon 2 /sbin/nologin
    adm 3 /sbin/nologin
    
    ➜  ~ awk -F':' '{print $1,$3,$NF}' passwd|column -t
    root             0      /bin/zsh
    bin              1      /sbin/nologin
    daemon           2      /sbin/nologin
    adm              3      /sbin/nologin
    lp               4      /sbin/nologin
    

    column -t 对齐

  • 指定复杂分隔符取出 ip

    1
    2
    3
    4
    5
    6
    7
    8
    
    ➜  ~ ip a s eth0 | awk 'NR==3{print $2}'
    172.18.243.120/20
    ➜  ~ ip a s eth0 | awk 'NR==3{print $2}' | awk -F'/' '{print $1}'
    172.18.243.120
    ➜  ~ ip a s eth0 | awk 'NR==3' | awk -F'[ /]' '{print $6}'
    172.18.243.120
    ➜  ~ ip a s eth0 | awk 'NR==3' | awk -F'[ /]+' '{print $3}'
    172.18.243.120
    

    image-20240721151206875

小结

  • 如果是空格,连续空格,直接使用 awk 取列即可
  • 其他情况需要通过 -F 指定分隔符加正则实现 []、 []+、 |

2.4 取行同时取列

  • 取 ip 地址

    1
    2
    
    ➜  ~ ip a s eth0 | awk -F'[ /]+' 'NR==3''{print $3}'
    172.18.243.120
    
  • 取出权限部分 stat /etc/hosts的 0644部分

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    
    ➜  ~ stat /etc/hosts
      文件:"/etc/hosts"
      大小:213       	块:8          IO 块:4096   普通文件
    设备:fd01h/64769d	Inode:262194      硬链接:1
    权限:(0644/-rw-r--r--)  Uid:(    0/    root)   Gid:(    0/    root)
    最近访问:2024-07-21 10:28:17.784671787 +0800
    最近更改:2024-07-04 10:26:00.231943650 +0800
    最近改动:2024-07-04 10:26:00.231943650 +0800
    创建时间:-
    
    ➜  ~ stat /etc/hosts | awk -F'[/(]' 'NR==4''{print $2}'
    0644
    ➜  ~ stat /etc/hosts | awk -F'[^0-9]+' 'NR==4''{print $2}'
    0644
    
  • 对列的判断,取出/etc/passwd 文件中第3列大于 1000的行,取出这一列,第 3 列和最后一列

    • 条件:$3
    • 动作:第 3 列和最后一列
    1
    2
    3
    4
    
    ➜  ~ awk -F':' '$3>1000''{print $1,$3,$NF}' /etc/passwd
    nfsnobody 65534 /sbin/nologin
    ➜  ~ awk -F':' '$3>1000''{print $1,$3,$NF}' /etc/passwd|column -t
    nfsnobody  65534  /sbin/nologin
    

    通过 awk 实现对某一列进行判断

  • 如果系统 swap 使用超过 0 则输出"异常系统开始占用 swap"

    1
    2
    
    ➜  ~ free |awk '/Swap/ && $3>=0 {print "系统异常"}'
    系统异常
    
  • 过滤出/etc/passwd第 4 列的数字是以 0 或 1 开头的行,输出第 1、3列

    • 之前^或$ 表示某一行的开头或结尾
    • 在 awk 中因为 awk 可以取列,通过列可以过滤某一列中包含什么 ,过滤某一列中以xxx开头或结尾
    1
    2
    3
    4
    5
    6
    
    awk -F':' '$4 ~ /^[01]/'
    ➜  ~ awk -F':' '$4 ~ /^[01]/''{print $1,$3}' passwd
    root 0
    bin 1
    sync 5
    shutdown 6
    

    awk 中,通过 $3 ~

    • ~ 表示包含的意思,$1 ~ /root/ 表示第一列中包含 root
    • !~ 表示不包含

小结

  • 取行: NR==1NR>=1$3 >=1000$3 ~ /root/
  • 取列:$1$NF$(NF-1)

2.5 统计与计算

统计次数

  • 仅仅需要统计出现了多少次,出现了多少个,可以使用 wc -l 方式
1
2
➜  ~ awk '{i=i+1} END {print i}' /etc/passwd
24

END{} 内容会在 awk 读取完文件的时候执行

END{} 一般用于输出执行结果

计算总和

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
➜  ~ cat num.txt
1
2
3
4
5
6
7
8
9
10
➜  ~ awk '{i=i+$1} END {print i}' num.txt
55

总结

  • awk 取行列
  • awk 进行对列的比较大小
  • awk 进行对列过滤
  • awk 计算、求和

image-20240721161727452

seq:输出数字序列

1
2
3
4
5
seq 10 

seq 5 10

seq 1 2 10