shell/shell-sed.md
2025-03-30 13:45:38 +08:00

338 lines
15 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<h2><center>Shell Sed编译器</center></h2>
------
## 一:非交互式编译器 sed
### 1. sed 介绍
Linux 中,常使用流编辑器`Sed`进行文本替换工作。与交互式编辑器如vim不同`Sed`编辑器以批处理的方式来编辑文件,这比交互式编辑器快得多,可以快速完成对数据的编辑修改。`Sed`编辑器会执行以下操作:
- 一次从输入中读取一行数据;
- 根据所提供的编辑器命令匹配数据;
- 按照命令修改流中的数据;
- 将新的数据输出到`STDOUT`
在 Sed 编辑器匹配完一行数据后,它会读取下一行数据并重复这个过程,直到处理完所有数据。使用 Sed 命令打开一个 Sed 编辑器。
![](http://182.92.143.66:40072/directlink/img/shell/images-202503290001.png)
`sed`是一种在线的、非交互式的编辑器,它一次处理一行内容。处理时,把当前处理的行存储在临时缓冲区中,称为"模式空间"`pattern space`),接着用`sed`命令处理缓冲区中的内容,处理完成后,把缓冲区的内容送往屏幕。接着处理下一行,这样不断重复,直到文件末尾。文件内容并没有改变,除非你使用重定向存储输出。`Sed`主要用来自动编辑一个或多个文件;简化对文件的反复操作;编写转换程序等。
### 2. 语法格式
```shell
sed [options] 'command' file
```
**options【选项】**
| 选项 | 作用 |
| :--: | :----------------------------------------------------------: |
| `-n` | 只有经过sed处理的行才会被列出 |
| `-e` | 允许多点编辑 |
| `-f` | 将sed的动作写在一个文本内-f filename 则可以执行filename内的sed动作。 |
| `-r` | 支持扩展正则 |
| `-i` | 直接修改源文件 |
**command【子命令】**
| 子命令 | 作用 |
| :----: | :------------------------------------------: |
| `a` | 在指定行**之后**追加文本 |
| `i` | 在指定行**之前**插入文本 |
| `c` | 替换整行内容 |
| `d` | 删除指定行 |
| `w` | 将匹配行的内容写入外部文件 |
| `y` | 按字符一一映射替换 |
| `p` | 打印匹配的行(通常与 `-n` 静默模式结合使用) |
| `r` | 从外部文件读取内容并插入到指定行后 |
| `q` | 匹配到指定行后立即退出处理 |
| `=` | 输出行号(行号单独显示,原行内容不变) |
| `s` | 替换匹配的文本 |
| `!` | 对匹配条件取反 |
| `g` | 在 `s` 命令中替换所有匹配项 |
| `数字` | 指定行号或范围 |
**特殊子命令**
| 子命令 | 作用 |
| :----: | :------------------------------------: |
| `h` | 将模式空间内容**覆盖**到保持空间 |
| `H` | 将模式空间内容**追加**到保持空间 |
| `g` | 用保持空间内容**覆盖**模式空间 |
| `G` | 将保持空间内容**追加**到模式空间 |
| `x` | 交换两个空间的内容 |
| `n` | 读取下一行覆盖当前模式空间 |
| `N` | 追加下一行到当前模式空间(合并多行) |
| `P` | 打印多行模式空间的第一行 |
| `D` | 删除多行模式空间的第一行并重新开始循环 |
### 3. 定址
默认`sed`对文件中的所有行进行编辑。当然,也可以只指定特定的某些行,或者行范围进行流编辑,这需要用到行寻址。所指定的行地址放在子命令之前
```shell
[address]command
```
**数字定址**
`sed`编辑器将文本流中的每一行都进行编号,第一行的编号为 1 ,后面的按顺序分配行号。通过指定特定的行号,可以选择编辑特定的行。
```bash
# 将第3行中所有的 bin 替换成 BIN
[root@wxin ~]# sed '3 s/bin/BIN/g' /etc/passwd
# 将第2到5行中所有的 bin 替换成 BIN
[root@wxin ~]# sed '2,5 s/bin/BIN/g' /etc/passwd
# 将第10行到最后一行中所有的 bin 替换成 BIN
[root@wxin ~]# sed '10,$ s/bin/BIN/g' /etc/passwd
```
**正则定址**
`sed`编辑器允许使用正则过滤出命令要作用的行
```shell
/pattern/command
```
必须使用" / " 将要指定的 pattern 包起来。sed 会寻找匹配文本模式的行,然后对这些行执行编辑命令
```bash
[root@wxin ~]# sed -n '/root/s/bin/BIN/p' /etc/passwd
# 寻找包含有字符串 root 的行,并将匹配行的 bin 替换为 BIN
```
与数字寻址一样,也可以使用正则过滤区间来过滤行。
```bash
[root@wxin ~]# sed '/pattern1/,/pattern2/ edit_command' file
```
这行命令会在文件 file 中先寻找匹配 pattern1 的行,然后从该行开始,执行编辑命令,直到找到匹配 pattern2 的行。但是需要注意的是使用文本区间过滤文本时只要匹配到了开始模式pattern1编辑命令就会开始执行直到匹配到结束模式pattern2这会导致一种情况一个文本中先匹配到了一对 pattern1、pattern2对该文本区间中的文本执行了编辑命令然后在 pattern2 之后又匹配到了 pattern1这时就会再次开始执行编辑命令因此在使用文本区间过滤时要注意这一点。
```bash
[root@wxin ~]# sed -n '/root/,/nologin/ s/bin/BIN/p' /etc/passwd
root:x:0:0:root:/root:/BIN/bash
BIN:x:1:1:bin:/bin:/sbin/nologin
operator:x:11:0:operator:/root:/sBIN/nologin
games:x:12:100:games:/usr/games:/sBIN/nologin
```
### 4. 示例
**`s`替换**
使用`s`命令来进行文本替换
```bash
[root@wxin ~]# sed 's/srcStr/dstStr/' file
```
其中,`srcStr`为想要替换的文本,`dstStr`为将要替换成的文本。使用`s`命令时,`sed`编辑器会在一行一行地读取文件`file`,并在每行查找文本`srcStr`,如果找到了,则将该处的`srcStr`替换为`dstStr`
`/`字符为界定符,用于分隔字符串(`sed`编辑器允许使用其他字符作为替换命令中的字符串分隔符):
```bash
[root@wxin ~]# sed 's!/bin/bash!/BIN/BASH!' /etc/passwd # 使用 ! 作为字符串分隔符
[root@wxin ~]# sed 's#3#88#g' datafile
```
默认,`s`只会替换掉目标文本在每行中第一次出现的地方。若想要替换掉每行中所有匹配的地方,可以使用替换标记`g`。替换标记放在编辑命令的末尾。除了`g`外,还有几种替换标记:
1. 数字指明替换掉第几次匹配到的文本
没有设置这个标记时,默认是替换第一次匹配的文本
```bash
[root@wxin ~]# sed 's/root/ROOT/2' /etc/passwd
```
这行命令将`/etc/passwd`文件中每行的第`2`个`root`替换为`ROOT`
2. g 替换所有匹配到的文本
```bash
[root@wxin ~]# sed 's/root/ROOT/g' /etc/passwd
```
这行命令将`/etc/passwd`文件中的`root`,全部替换为`ROOT`
3. p 打印与替换命令中指定模式srcStr相匹配的行
```bash
[root@wxin ~]# sed 's/root/ROOT/p' /etc/passwd
```
执行这命令,会在 STDOUT 上看到包含有 root 的行被输出了两次,一次是 sed 编辑器自动输出的;另一次则是 p 标记打印出来的匹配行
```bash
[root@wxin ~]# sed -n 's/root/ROOT/gp' /etc/passwd    # 将 /etc/passwd 中所有的 root 都替换成 ROOT并输出被修改的行
```
**`d`删除**
`sed`编辑器使用`d`命令来删除文本流中的特定行。使用`d`命令时,一般需要带上位寻址,以删除指定的行,否则默认会删除所有文本行。
```bash
[root@wxin ~]# sed '/root/d' /etc/passwd  # 删除匹配 root 的行
[root@wxin ~]# sed '2,$d' /etc/passwd    # 删除第2到最后一行
[root@wxin ~]# sed '3d' /etc/passwd
[root@wxin ~]# sed '3{d;}' /etc/passwd
[root@wxin ~]# sed '3{d}' /etc/passwd
[root@wxin ~]# sed '$d' /etc/passwd
[root@wxin ~]# sed '/north/d' /etc/passwd
[root@wxin ~]# sed '/sout/d' /etc/passwd
```
**插入追加修改文本**
`sed`编辑器使用`i`命令来向数据流中插入文本行,使用`a`命令来向数据流中附加文本行。其中`i`命令会在指定行前增加一个新行;`a`命令会在指定行后增加一个新行。
注意:
这两个命令都不能在单行上使用(即不是用来在一行中插入或附加一段文本的),只能指定插入还是附加到另一行。
```bash
[root@wxin ~]# sed '[address][i | a] newline' file
[root@wxin ~]# sed 'i\Insert a line behind every line' /etc/passwd      # 向数据流的每一行前面增加一个新行,新行的内容为 \ 后面的内容
[root@wxin ~]# sed '1i\Insert a line behind the first line' /etc/passwd   # 在数据流的第一行前面增加一个新行
[root@wxin ~]# sed '3a\Append a line after the third line' /etc/passwd # 在数据流的第三行后面增加一个新行    
[root@wxin ~]# sed '$a\Append a line in the last line' /etc/passwd      # 在数据流的最后一行后面增加一个新行
```
使用命令`c`可以将数据流中的整行文本修改为新的行,与插入、附加操作一样,这要求在`sed`命令中指定新的行
```bash
[root@wxin ~]# sed '[address][c] newtext' file
[root@wxin ~]# sed '3 c\New text' /etc/passwd     # 将数据流中第三行的内容修改为 \ 后面的内容
[root@wxin ~]# sed '/root/ c\New text' /etc/passwd  # 将匹配到 root 的行的内容修改为 \ 后面的内容
[root@wxin ~]# sed '2,4c\New text' /etc/passwd     # 将第2到4行的内容修改为 \ 后面的内容,但是不是逐行修改,而是会将这之间的 3 行用一行文本来替代
```
**`y`逐字符转换**
```shell
[address]y/inchars/outchars/
```
转换命令会对 inchars 和 outchars 的值进行一对一的映射。inchars 中的第一个字符会被转换成 outchars 中的第一个字符inchars 中的第二个字符会被转换成 outchars 中的第二个字符;... 直到处理完一行。如果 inchars 和 outchars 的长度不同,则 sed 编辑器会产生一个错误消息。举个例子:
```bash
[root@wxin ~]# echo abcdefggfedcba | sed 'y/acg/ACG/'
AbCdefGGfedCbA
```
**`w`保存数据到文件**
```bash
[address]w filename
[root@wxin ~]# sed '1,2w test.txt' /etc/passwd
[root@wxin ~]# sed -n 's/root/ROOT/g w change.txt' /etc/passwd # 将 /etc/passwd 中所有的 root 都替换成 ROOT,并将被修改的行保存到文change.txt 中去
```
该语句将数据流的第 1、2 行写入文件 test.txt 中去。
**`r`从文件中读取数据**
可以使用 r 命令来将一个文本中的数据插入到数据流中去,与普通的插入命令 i 类似,这也是对行进行操作的,命令格式如下:
```shell
[address]r filename
```
filename 为要插入的文件。r 命令常结合行寻址使用,以将文本插入到指定的行后面。
```bash
[root@wxin ~]# sed '3 r test.txt' /etc/passwd
```
这句话将文件 test.txt 中的内容插入到数据流第三行后面去。
### 5. 模式空间和保持空间
模式空间和保持空间是两个独立的缓冲区,可以进行交互,命令可以寻址模式空间但是不能寻址保持空间。
- 模式空间:容纳当前输入行的缓冲区,通过模式匹配到的行被读入模式空间中。用来进行进一步的操作;在多行模式中,`\n`可以用来和模式空间(`N`命令的结果)的任意换行符匹配,单模式空间底部的换行符除外。`^`匹配多行的首,`$`匹配多行的尾,不是每行的行首和行尾
- 保持空间:`sed`在处理文本的时候都是在模式空间中进行,但有时候有些复杂的操作单一的模式空间可能无法满足需求,于是就有了保持空间,这个空间通常是空闲的,并不处理数据,只在有需要的时候和模式空间进行一些必要的数据交换。
下面是模式空间中的常用命令。
- h: 把模式空间中的内容覆盖至保持空间中
- H把模式空间中的内容追加至保持空间中
- g: 从保持空间取出数据覆盖至模式空间
- G从保持空间取出内容追加至模式空间
- x: 把模式空间中的内容与保持空间中的内容进行互换
**多行模式空间**
sed命令都是一行一行的进行处理文本的不过有些时候单行处理可能并不能满足我们的需要所以sed还提供了多行模式多行模式的命令主要有 NPD 三个
- N读取匹配到的行的下一行追加至模式空间
- P打印模式空间开端至\n内容并追加到默认输出之前
- D如果模式空间包含换行符则删除直到第一个换行符的模式空间中的文本 并不会读取新的输入行,而使用合成的模式空间重新启动循环。如果模式空间 不包含换行符则会像发出d命令那样启动正常的新循环
N命令流程图P 和 D 命令在懂了N命令之后也容易理解。
![](http://182.92.143.66:40072/directlink/img/shell/images-202503290002.jpeg)
**示例:**
```bash
[root@wxin ~]# cat /etc/passwd |sed -n '2{N;p}'
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
# 读取第二行的下一行,然后输出模式空间中的内容,此时模式空间中有两行
[root@wxin ~]# cat /etc/passwd |sed -n '2{N;N;N;p}'
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
# 使用多个N命令可以读取多行进模式空间
[root@wxin ~]# seq 1 6| sed -n '1,2H;4p;5{x;p}'
4
1
2
```
-n 是不显示默认输出内容1,2H是将前两行追加至保持空间4p显示第四行5{x;p}是在第五行的时候交换保持空间和模式空间中的内容并且输出。注意输出中的空行这是因为H命令追加的时候是添加换行符由于保持空间默认是空的所以添加换行符之后就多了一个空行。以用下面的命令先往保持空间覆盖一行然后追加。
```bash
[root@wxin ~]# seq 1 6| sed -n '1h;2H;4p;5{x;p}'
4
1
2
```
- 第一个循环结束之后:模式空间为空,保持空间为第一行内容
- 第二个循环,将第二行追加到模式空间,此时模式空间为两行内容
- 第三个循环,没有匹配内容,不执行操作,模式空间和保持空间内容不变
- 第四个循环,读取第四行并输出,保持空间内容不变
- 第五个循环,读入第五行,然后和保持空间中的内容交换,之后输出。
```bash
# 暂存和取用命令h H g G
[root@wxin ~]# sed -r '1h;$G' /etc/hosts
[root@wxin ~]# sed -r '1{h;d};$G' /etc/hosts
[root@wxin ~]# sed -r '1h; 2,$g' /etc/hosts
[root@wxin ~]# sed -r '1h; 2,3H; $G' /etc/hosts
# 暂存空间和模式空间互换命令x
[root@wxin ~]# sed -r '4h; 5x; 6G' /etc/hosts
```