Skip to content

sed

字数: 12920 字 时长: 47 分钟

sed 是一种流式文本编辑器,它是文本处理中非常强大的工具,能够与正则表达式完美结合,实现高效且灵活的文本操作。sed 在处理文本时,会逐行读取输入内容,将当前处理的行存储在临时缓冲区中,这个缓冲区也被称为模式空间(Pattern Space)。随后,sed 会根据指定的命令对模式空间中的内容进行处理。处理完成后,将模式空间中的内容输出到屏幕上,接着读取下一行并重复上述过程,直到处理完文件中的所有行。

默认情况下,sed 不会直接修改原始文件的内容,而是仅对模式空间中的数据进行操作。如果需要修改原始文件,可以通过特定选项(如 -i)来实现。

流式编辑器

流式(Stream)文本编辑器是指对文本数据进行逐行处理的工具,它将文本视为一个连续的「流」,而不是一次性加载整个文件进行处理。这种处理方式类似于水流一样,逐行读取、处理并输出结果。

sed 的处理流程可以总结为以下步骤:

  1. 逐行读取:每次仅读取一行内容到模式空间。
  2. 匹配与修改:根据提供的规则命令对模式空间中的数据进行匹配和修改。需要注意的是,sed 默认不会直接修改源文件数据,而是将数据复制到模式空间中进行操作。
  3. 输出结果:将模式空间中修改后的数据输出到标准输出(通常是屏幕)。
  4. 循环处理:处理完当前行后,继续读取下一行并重复上述过程,直到处理完文件中的所有行。

sed 的模式匹配和文本选择功能高度依赖于正则表达式(Regexes),这使得它能够灵活地处理各种复杂的文本模式。通过正则表达式,用户可以精确地指定需要匹配的文本模式,从而实现高效的查找、替换、删除等操作。

基本语法

sed 的基本语法如下:

shell
sed [OPTION]... {script-only-if-no-other-script} [input-file]...
  • script-only-if-no-other-script:如果未使用 -e-f 选项指定其他脚本,则可以在这里直接指定一个 sed 脚本。脚本可以是一个简单的命令,也可以是多个命令的组合。
  • input-file:指定要处理的输入文件。可以同时指定多个文件,sed 会依次处理每个文件。
常用选项描述
-n禁止自动打印模式空间:默认情况下,sed 会自动打印处理后的每一行。使用 -n 选项后,sed 不会自动打印任何内容,只有在脚本中明确使用 p 命令时才会打印指定的行。这在需要精确控制输出时非常有用。
-e <script>添加脚本到命令中:允许在命令行中直接指定一个或多个 sed 脚本。如果需要执行多个脚本命令,可以多次使用 -e 选项。例如:sed -e 's/a/b/' -e 's/c/d/'
-f <script-file>从文件中读取脚本:指定一个包含 sed 脚本的文件。脚本文件中的命令会逐行执行。这在处理复杂的脚本时非常方便,可以将脚本写入文件中,然后通过 -f 选项调用。
-i[SUFFIX]直接编辑文件:允许直接修改输入文件,而不是将结果输出到标准输出。如果提供了 SUFFIX,则会创建一个备份文件,备份文件的扩展名为指定的 SUFFIX。例如:sed -i.bak 's/old/new/' file.txt 会将 file.txt 中的内容替换,并创建一个名为 file.txt.bak 的备份文件。
-c使用复制而不是重命名:在使用 -i 模式时,sed 默认通过重命名文件来实现原地编辑。使用 -c 选项后,sed 会通过复制原始文件来实现备份,而不是重命名。这种方式在某些情况下可以避免文件权限问题。
-b不执行任何操作:这个选项通常用于测试,它不会对文件进行任何实际操作,但会显示 sed 命令的执行效果。
-r-E使用扩展正则表达式:默认情况下,sed 使用基本正则表达式(BRE)。使用 -r(GNU 扩展)或-E(POSIX 扩展)选项后,可以启用扩展正则表达式(ERE),从而支持更复杂的正则表达式语法。为了确保跨平台兼容性,建议使用 -E 选项。
-s将文件视为独立的流:默认情况下,sed 将多个文件视为一个连续的长流,这意味着跨文件的模式空间是连续的。使用 -s 选项后,sed 会将每个文件视为独立的流,每个文件的模式空间是独立的。这在处理多个文件时非常有用,可以避免跨文件的模式空间混淆。

sed 脚本

sed 脚本是一系列 sed 命令的集合,它们指定了要对输入文本执行的操作。脚本中的每个命令通常由一个字母(如 sdp)和一些参数组成,用于指定操作的类型以及操作的文本范围。通过组合这些命令,sed 可以实现复杂的文本处理任务。

注意,sed 脚本通常需要使用单引号 '' 或双引号 "" 来包裹命令,以确保命令中的特殊字符(如空格、美元符号、反斜杠等)被正确处理。使用单引号 '' 是最常见的做法,因为它可以防止 Shell 对脚本中的内容进行额外的解释。

sed 脚本语法:

shell
[address]command[arguments]
  • address:可选的地址范围,用于指定命令作用的行。可以是行号、正则表达式或两者的组合。
  • commandsed 命令,如 p(打印)、d(删除)、s(替换)等。
  • arguments:命令的参数,具体取决于命令类型。

地址范围(Address):

地址范围用于指定命令作用的行。可以是以下几种形式:

  • 行号:指定特定行。例如,1 表示第一行,5 表示第五行。
  • 正则表达式:用 /pattern/ 表示匹配特定模式的行。例如,/root/ 表示匹配包含 root 的行。
  • 地址范围:用 M,N 表示从第 M 行到第 N 行。例如,1,5 表示从第一行到第五行。
  • 特殊符号:$:表示最后一行;~:用于指定步长。例如,1~2 表示从第一行开始,每隔一行。

sed 命令:

常用的命令描述
a在当前行下面插入文本:在当前行的下方插入指定的文本。例如,a\Hello 会在当前行的下方插入 Hello。
i在当前行上面插入文本:在当前行的上方插入指定的文本。例如,i\Hello 会在当前行的上方插入 Hello。
c替换当前行:将当前行替换为指定的文本。例如,c\New Line 会将当前行替换为 New Line。
d删除当前行:删除当前行的内容。例如,d 会删除当前行。
s替换指定字符:这是 sed 最常用的命令之一,用于替换文本中的某些内容。格式为 s/模式/替换内容/ 标志。例如,s/old/new/g 会将每行中的所有 old 替换为 new
l可视化方式打印模式空间中的文本:以可视化方式打印模式空间中的文本,不可打印字符会以反斜杠转义序列的形式显示。例如,l 可以用来查看模式空间中的特殊字符。
n读取下一行输入:读取下一行输入并将其存储在模式空间中,覆盖当前模式空间的内容。例如,n 可以用于跳过当前行,处理下一行。
p打印模式空间中的文本:打印当前模式空间中的内容。默认情况下,sed 会自动打印每一行,但使用 -n 选项时,只有被 p 命令标记的行才会被打印。
q退出 sed:退出 sed 程序。通常用于在处理完特定行后立即退出。

示例文本:

text
1. This is the first line.

2. This line contains the word "delete".
3. This is the third line.

4. This line should be removed.
# 5. This is the last line.

p(打印)

p 命令用于搜索符合符号条件的行,并输出该行的内容,其基本格式为:

shell
[address]p

常见的用法是打印包含匹配文本模式的行,通常与 -n 选项配合使用,这样可以禁止输出其他行,仅打印包含匹配文本模式的行。如果不使用 -n 选项,sed 不仅会打印匹配 p 命令的每一行,还会打印文件的所有内容,因为 sed 本身在处理完每一行后会自动打印模式空间的内容。

  1. 打印文件中的所有行

    shell
    [root@localhost ~]# sed -n 'p' example.txt 
    1. This is the first line.
    
    2. This line contains the word "delete".
    3. This is the third line.
    
    4. This line should be removed.
    # 5. This is the last line.
  2. 打印文件中的第一行

    shell
    [root@localhost ~]# sed -n '1p' example.txt 
    1. This is the first line.
  3. 打印文件中的第一行到第五行

    shell
    [root@localhost ~]# sed -n '1,5p' example.txt 
    1. This is the first line.
    
    2. This line contains the word "delete".
    3. This is the third line.
  4. 打印文件中的第一行和第五行

    shell
    [root@localhost ~]# sed -n '1p;5p' example.txt 
    1. This is the first line.
  5. 打印文件中除了第一行的内容

    ! 表示取反,使用 1!p 可以实现此功能:

    shell
    [root@localhost ~]# sed -n '1!p' example.txt 
    
    2. This line contains the word "delete".
    3. This is the third line.
    
    4. This line should be removed.
    # 5. This is the last line.
  6. 使用扩展的正则表达式

    当需要使用扩展的正则表达式时,需要加上 -r 选项:

    shell
    [root@localhost ~]# sed -nr '/1|2|3/p' example.txt 
    1. This is the first line.
    2. This line contains the word "delete".
    3. This is the third line.

    例如,$ 表示最后一行,= 表示行号,打印文件中最后一行的行号:

    shell
    [root@localhost ~]# sed -nr '$=' example.txt 
    7

    使用正则打印文件中包含 third 的行:

    shell
    [root@localhost ~]# sed -nr '/third/p' example.txt 
    3. This is the third line.

d(删除)

如果需要删除文本中的特定行,可以使用 d 脚本命令,它会删除指定行中的所有内容,基本格式为:

shell
[address]d

当和指定地址一起使用时,删除命令能发挥出强大的功用,可以从数据流中删除特定的文本行。但使用该命令时要特别小心,如果忘记指定具体行,文件中的所有内容都会被删除。

  1. 删除特定行

    删除文件中的第 3 行:

    shell
    [root@localhost ~]# sed '3d' example.txt
    1. This is the first line.
    
    3. This is the third line.
    
    4. This line should be removed.
    # 5. This is the last line.
  2. 删除多个特定行

    删除文件中的第 2 行和第 3 行:

    shell
    [root@localhost ~]# sed '2d;3d' example.txt
    1. This is the first line.
    3. This is the third line.
    
    4. This line should be removed.
    # 5. This is the last line.
  3. 删除匹配模式的行

    删除包含单词 delete 的行:

    shell
    [root@localhost ~]# sed '/delete/d' example.txt
    1. This is the first line.
    
    3. This is the third line.
    
    4. This line should be removed.
    # 5. This is the last line.
  4. 删除多个匹配模式的行

    删除包含 deleteremoved 的行:

    shell
    [root@localhost ~]# sed '/delete/d;/removed/d' example.txt
    1. This is the first line.
    
    2. This is the third line.
    
    # 5. This is the last line.
  5. 删除空行

    删除文件中的所有空行:

    shell
    [root@localhost ~]# sed '/^$/d' example.txt
    1. This is the first line.
    2. This line contains the word "delete".
    3. This is the third line.
    4. This line should be removed.
    # 5. This is the last line.

    sed '/^$/d' 命令通过正则表达式 /^$/ 匹配空行。^ 表示行的开头;$ 表示行的结尾。如果一个行只包含行的开头和结尾,而中间没有任何字符,那么这个行就是空行。因此,/^$/ 可以精确地匹配那些没有任何字符的空行。

  6. 删除以特定字符开头的行

    删除以 # 开头的注释行:

    shell
    [root@localhost ~]# sed '/^#/d' example.txt
    1. This is the first line.
    
    2. This line contains the word "delete".
    3. This is the third line.
    
    4. This line should be removed.

sed 的删除命令 d 非常灵活,可以结合行号、正则表达式等条件来精确地删除指定的行。需要强调的是,在默认情况下 sed 并不会修改原始文件,这里被删除的行只是从 sed 的输出中消失了,原始文件并未做任何改变。如果想要修改原始文件,则需要加上 -i 选项。

s(替换)

sedreplace 命令(s)是其最强大且最常用的命令之一,用于替换文本中的某些内容。它支持正则表达式,可以实现非常灵活的文本替换操作。基本格式为:

shell
[address]command/pattern/replacement/flags
  • address:可选的地址范围,用于指定命令作用的行。可以是行号、正则表达式或两者的组合。
  • pattern:要匹配的内容,可以是普通字符串或正则表达式。
  • replacement:替换后的内容,可以包含普通字符或特殊字符(如 \1\2 等,用于引用匹配的子表达式)。
  • flags:可选的标志,用于修改替换操作的行为。

sedflags 介绍:

标志描述
n1~512 之间的数字,表示指定要替换的字符串出现第几次时才进行替换,例如,一行中有 3 个 A,但用户只想替换第二个 A,这是就用到这个标记;
g对数据中所有匹配到的内容进行替换,如果没有 g,则只会在第一次匹配成功时做替换操作。例如,一行数据中有 3 个 A,则只会替换第一个 A
p会打印与替换命令中指定的模式匹配的行。此标记通常与 -n 选项一起使用。
w将缓冲区中的内容写到指定的文件中。
y表示把一个字符翻译为另外的字符(但是不用于正则表达式)
\n匹配第 n 个子串,该子串之前在 pattern 中用 \(\) 指定。
&用正则表达式匹配的内容进行替换。

需要注意的是,flags 不仅仅针对替换命令,它是一些可选标志,可以用于多种 sed 命令,当然不同的命令支持的 flags 也不尽相同。

替换功能实例:

text
This is a test of the trial script.
This is the second test of the trial script.
This is a different line.
  1. 指定替换次数

    可以指定 sed 用新文本替换第几处模式匹配的地方:

    shell
    [root@localhost ~]# sed 's/test/trial/2' flags.txt
    This is a test of the trial script.
    This is the second test of the trial script.
    This is a different line.

    可以看到,使用数字 2 作为标志的结果就是,sed 编辑器只替换每行中第 2 次出现的匹配模式。

  2. 全局替换

    如果要用新文件替换所有匹配的字符串,可以使用 g 标记:

    shell
    [root@localhost ~]# sed 's/test/trial/g' flags.txt
    This is a trial of the trial script.
    This is the second trial of the trial script.
    This is a different line.
  3. 仅输出被替换的行

    -n 选项会禁止 sed 输出,但 p 标记会输出修改过的行,将二者配合使用的效果就是只输出被替换命令修改过的行:

    shell
    [root@localhost ~]# sed -n 's/test/trial/p' flags.txt
    This is a trial of the trial script.
    This is the second trial of the trial script.
  4. 保存匹配结果到指定文件

    w 标记会将匹配后的结果保存到指定文件中:

    shell
    [root@localhost ~]# sed 's/test/trial/w flags1.txt' flags.txt
    This is a trial line.
    This is a different line.
    shell
    [root@localhost ~]#cat test.txt
    This is a trial line.
  5. 处理文件路径替换

    在使用 s 脚本命令时,替换类似文件路径的字符串会比较麻烦,需要将路径中的正斜线进行转义:

    shell
    [root@localhost ~]# sed 's/\/bin\/bash/\/sbin\/sh/' /etc/passwd

    使用转义符可以成功,但非常不方便。最佳方案是使用替换符号,可以用数字上的所有符号代替:

    shell
    [root@localhost ~]# sed 's!/bin/bash!/sbin/sh!' /etc/passwd
    [root@localhost ~]# sed 's@/bin/bash@/sbin/sh@' /etc/passwd

参考案例:

text
2017 2011 2018
2017 2017 2024
2017 2017 2017
  1. 替换每行的第一个匹配项

    将文件中的所有行的第一个 2017 替换成 abcd,由于没有指定地址,就是所有行都进行替换,但是只会替换行中的第一个:

    shell
    [root@localhost ~]# sed 's/2017/abcd/' sed.txt
    abcd 2011 2018
    abcd 2017 2024
    abcd 2017 2017
  2. 替换指定行的第一个匹配项

    将文件中的第二行的第一个 2017 替换成 hello

    shell
    [root@localhost ~]# sed '2s/2017/hello/' sed.txt
    2017 2011 2018
    hello 2017 2024
    2017 2017 2017
  3. 替换指定行的指定次匹配项

    将文件中的第二行的第二个 2017 替换成 hello

    shell
    [root@localhost ~]# sed '2s/2017/hello/2' sed.txt
    2017 2011 2018
    2017 hello 2024
    2017 2017 2017
  4. 替换符合条件行的指定次匹配项

    匹配以 2017 结尾的行,将第三个 2017 替换为 abcd

    shell
    [root@localhost ~]# sed '/2017$/s/2017/abcd/3' sed.txt
    2017 2011 2018
    2017 2017 2024
    2017 2017 abcd
  5. 替换符合条件行的所有匹配项

    匹配以 2017 结尾的行,将全部的 2017 替换为 abcd

    shell
    [root@localhost ~]# sed '/2017$/s/2017/abcd/g' sed.txt
    2017 2011 2018
    2017 2017 2024
    abcd abcd abcd

a 和 i(添加)

sed 的添加功能(ai 命令)用于在指定行的上方或下方插入文本。

  • 在指定行下方插入文本(a命令):
shell
[address]a\text
  • 在指定行上方插入文本(i 命令):
shell
[address]i\text

注意,text 前面的反斜杠 \ 是必需的,用于指示 sed 在下一行继续读取命令。如果需要插入多行文本,可以使用多个反斜杠 \ 来分隔每行。

示例文本:

text
1. This is the first line.

2. This line contains the word "delete".
3. This is the third line.

4. This line should be removed.
# 5. This is the last line.
  1. 在指定行下方插入文本

    在第 2 行下方插入 Hello, world!

    shell
    [root@localhost ~]# sed '2a\Hello, world!' example.txt
    1. This is the first line.
    
    Hello, world!
    2. This line contains the word "delete".
    3. This is the third line.
    
    4. This line should be removed.
    # 5. This is the last line.
  2. 在指定行上方插入文本

    在第 3 行上方插入 Hello, world!

    shell
    [root@localhost ~]# sed '3i\Hello, world!' example.txt
    1. This is the first line.
    
    Hello, world!
    2. This line contains the word "delete".
    3. This is the third line.
    
    4. This line should be removed.
    # 5. This is the last line.
  3. 在匹配模式的行下方插入文本

    在包含 delete 的行下方插入 This line will be marked.

    shell
    [root@localhost ~]# sed '/delete/a\This line will be marked.' example.txt
    1. This is the first line.
    
    2. This line contains the word "delete".
    This line will be marked.
    3. This is the third line.
    
    4. This line should be removed.
    # 5. This is the last line.
  4. 在文件末尾插入文本

    在文件末尾插入 This is the new last line.

    shell
    [root@localhost ~]# sed '$a\This is the new last line.' example.txt
    1. This is the first line.
    
    2. This line contains the word "delete".
    3. This is the third line.
    
    4. This line should be removed.
    # 5. This is the last line.
    This is the new last line.

c(整行替换)

sedc 命令用于将指定行的整个内容替换为新的文本。这个命令会完全删除指定行的内容,并用新的文本替换。基本格式为:

shell
[address]c\text

示例文本:

text
1. This is the first line.

2. This line contains the word "delete".
3. This is the third line.

4. This line should be removed.
# 5. This is the last line.
  1. 替换特定行

    将第 3 行替换为 New content here.

    shell
    [root@localhost ~]# sed '3c\New content here.' example.txt
    1. This is the first line.
    
    New content here.
    3. This is the third line.
    
    4. This line should be removed.
    # 5. This is the last line.
  2. 替换匹配模式的行

    将包含 third 的行替换为 New content here.

    shell
    [root@localhost ~]# sed '/third/c\New content here.' example.txt
    1. This is the first line.
    
    2. This line contains the word "delete".
    New content here.
    
    4. This line should be removed.
    # 5. This is the last line.
  3. 替换最后一行

    将最后一行替换为 This is the new last line.

    shell
    [root@localhost ~]# sed '$c\This is the new last line.' example.txt
    1. This is the first line.
    
    2. This line contains the word "delete".
    3. This is the third line.
    
    4. This line should be removed.
    This is the new last line.

y(转换)

sedy 命令用于对文本中的字符进行一对一的转换。它是一个非常强大的工具,可以快速地将文本中的某些字符替换为其他字符。基本格式为:

shell
[address]y/inchars/outchars/
  • inchars:要被替换的字符集合。
  • outchars:替换后的字符集合。

sedy 命令会对 incharsoutchars 中的字符进行一对一的映射。例如,inchars 中的第一个字符会被替换为 outchars 中的第一个字符,第二个字符会被替换为 outchars 中的第二个字符,依此类推。如果 incharsoutchars 的长度不同,sed 会产生一条错误消息。

注意,转换命令是一个全局命令,也就是说,它会文本行中找到的所有指定字符自动进行转换,而不会考虑它们出现的位置。

示例文本:

text
This is line number 1 OK?.
This is line number 2.
This is line number 3.
  1. 替换特定字符

    将所有 1 替换为 72 替换为 83 替换为 9

    shell
    [root@localhost ~]# sed 'y/123/789/' example2.txt
    This is line number 7 OK?.
    This is line number 8.
    This is line number 9.
  2. 替换指定行中的字符

    只对第 2 行进行字符替换:

    shell
    [root@localhost ~]# sed '2y/123/789/' example2.txt
    This is line number 1 OK?.
    This is line number 8.
    This is line number 3.
  3. 替换匹配模式的行中的字符

    对包含 OK 的行进行字符替换:

    shell
    [root@localhost ~]# sed '/OK/y/123/789/' example2.txt
    This is line number 7 OK?.
    This is line number 2.
    This is line number 3.

w (写入文件)

sed 的 w 命令用于将模式空间中的内容写入到指定的文件中。基本格式为:

shell
[address1[,address2]]w file
  • file:目标文件路径,也就是内容要备份/拷贝到的目标文件地址。
  • address1address2:分别是起始地址结束地址 ,可以是行号或模式字符串。file 可以使用相对路径或绝对路径,但不管是哪种,运行 sed 命令的用户都必须有文件的写权限。

address1address2 都是可选参数,可以都不填,这时候就是全文件拷本/备份了。如果存在一个,那么就是备份单行。

w 命令会将模式空间中的内容写入到指定的文件中。如果目标文件不存在,sed 会创建该文件。如果目标文件已经存在,sed 会覆盖原文件的内容,而不是追加。

示例文本:

text
1) 小明,23 岁,北京大学
2) 小红,22 岁,清华大学
3) 小李,25 岁,斯坦福大学
4) 小王,22 岁,清华大学
  1. 将整个文件的内容写入目标文件

将整个 example3.txt 文件的内容写入到 output.txt 中:

shell
[root@localhost ~]# sed 'w output.txt' example3.txt
1) 小明,23 岁,北京大学
2) 小红,22 岁,清华大学
3) 小李,25 岁,斯坦福大学
4) 小王,22 岁,清华大学

sed 在处理文件时,默认会将处理后的每一行输出到标准输出(通常是屏幕)。即使使用了 w 命令将某些行写入到文件中,sed 仍然会将所有行输出到标准输出,除非明确禁止这种行为。只想将内容写入到文件中,而不希望看到标准输出,可以使用 -n 选项来禁止默认输出。

shell
[root@localhost ~]# cat output.txt 
1) 小明,23 岁,北京大学
2) 小红,22 岁,清华大学
3) 小李,25 岁,斯坦福大学
4) 小王,22 岁,清华大学
  1. 将第 3 行写入目标文件

将第 3 行的内容写入到 output.txt 中:

shell
[root@localhost ~]# sed -n '3w output.txt' example3.txt
shell
[root@localhost ~]# cat output.txt 
3) 小李,25 岁,斯坦福大学
  1. 将匹配模式的行写入目标文件

将包含清华大学的行写入到 output.txt 中:

shell
sed -n '/清华大学/w output.txt' example3.txt
shell
[root@localhost ~]# cat output.txt 
2) 小红,22 岁,清华大学
4) 小王,22 岁,清华大学

r(从文件中读取内容)

sed 的 r 命令用于从指定的文件中读取内容,并将其插入到模式空间中指定的位置。基本格式为:

shell
[address]r file

r 命令会将指定文件的内容读取并插入到模式空间中指定行的下方。如果 address 省略,则默认在所有行之后插入文件内容;如果 address 指定为行号或正则表达式,则只在匹配的行之后插入文件内容。

示例文本:

text
1) 小明,23 岁,北京大学
2) 小红,22 岁,清华大学
3) 小李,25 岁,斯坦福大学
4) 小王,22 岁,清华大学
text
这是插入的内容
  1. 在指定行之后插入文件内容

    在 example3.txt 的第 2 行之后插入 insert.txt 的内容:

    shell
    [root@localhost ~]# sed '2r insert.txt' example3.txt
    1) 小明,23 岁,北京大学
    2) 小红,22 岁,清华大学
    这是插入的内容
    3) 小李,25 岁,斯坦福大学
    4) 小王,22 岁,清华大学
    shell
    [root@localhost ~]# cat example3.txt 
    1) 小明,23 岁,北京大学
    2) 小红,22 岁,清华大学
    3) 小李,25 岁,斯坦福大学
    4) 小王,22 岁,清华大学

    注意,sed 在处理文件时,默认会将处理后的每一行输出到标准输出(通常是屏幕),但不会直接修改原始文件,除非明确指定使用 -i 选项。因此,看到的输出是处理后的结果,但 example3.txt 文件本身并没有被修改。

  2. 在匹配模式的行之后插入文件内容

    在包含清华大学的行之后插入 insert.txt 的内容:

    shell
    [root@localhost ~]# sed '/清华大学/r insert.txt' example3.txt
    1) 小明,23 岁,北京大学
    2) 小红,22 岁,清华大学
    这是插入的内容
    3) 小李,25 岁,斯坦福大学
    4) 小王,22 岁,清华大学
    这是插入的内容
  3. 在文件末尾插入文件内容

    在 example3.txt 的末尾插入 insert.txt 的内容:

    shell
    [root@localhost ~]# sed '$r insert.txt' example3.txt
    1) 小明,23 岁,北京大学
    2) 小红,22 岁,清华大学
    3) 小李,25 岁,斯坦福大学
    4) 小王,22 岁,清华大学
    这是插入的内容

q(退出脚本)

sedq 命令用于在处理完指定的行或模式后立即退出 sed 脚本,其基本格式为:

shell
[address]q [exit code]
  • exit code:可选的退出状态码,用于指定 sed 程序退出时的状态码。

q 命令会在到达指定的行或匹配模式后立即退出 sed 脚本,不再处理后续的行。如果没有指定 addresssed 会在处理完第一行后立即退出。如果指定了 exit codesed 会以该状态码退出。

这个命令在处理大文件时非常有用,因为它可以让快速地查看文件的前几行内容,而不需要等待整个文件被处理完毕。如果只想查看一个大文件的前 10 行内容,可以使用下面的命令:

shell
[root@localhost ~]# sed '2q' /etc/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
  1. 在匹配模式的行之后退出

    在匹配到北京大学的行之后退出 sed 脚本:

    shell
    [root@localhost ~]# sed '/北京大学/q' example3.txt
    1) 小明,23 岁,北京大学
  2. 指定退出状态码

    在匹配到斯坦福大学的行之后退出 sed 脚本,并指定退出状态码为 100:

    shell
    [root@localhost ~]# sed '/斯坦福大学/q 100' example3.txt
    1) 小明,23 岁,北京大学
    2) 小红,22 岁,清华大学
    3) 小李,25 岁,斯坦福大学

    可以使用 $? 来检查刚刚的退出码:

    shell
    [root@localhost ~]# echo $?
    100

参考示例

修改 Yum 软件仓库的配置文件,将默认的镜像源地址从 http://dl.rockylinux.org/ 替换为阿里云的镜像源地址 https://mirrors.aliyun.com/rockylinux,从而加快软件包的下载速度。

shell
sed -e 's|^mirrorlist=|#mirrorlist=|g' \
    -e 's|^#baseurl=http://dl.rockylinux.org/$contentdir|baseurl=https://mirrors.aliyun.com/rockylinux|g' \
    -i.bak \
    /etc/yum.repos.d/[Rr]ocky*.repo
  • -e:允许在命令行中指定多个编辑命令。在此案例中,使用了两个 -e 选项来执行两个不同的替换操作。
  • 's|^mirrorlist=|#mirrorlist=|g':这是第一个编辑命令,使用了 s(替换)命令。^ 表示行首,mirrorlist= 是要被替换的字符串,#mirrorlist= 是替换后的内容。g 表示全局替换,即对每一行中所有匹配的内容进行替换。此命令的作用是将所有以 mirrorlist= 开头的行替换为以 #mirrorlist= 开头,从而注释掉这些行。
  • 's|^#baseurl=http://dl.rockylinux.org/$contentdir|baseurl=https://mirrors.aliyun.com/rockylinux|g':这是第二个编辑命令,同样使用了 s 命令。它将匹配到的 #baseurl= 开头的行替换为新的 baseurl= 内容,从而将默认的镜像源地址 http://dl.rockylinux.org/ 替换为 https://mirrors.aliyun.com/rockylinux
  • -i.bak:表示直接在原始文件上进行编辑,并创建一个带有 .bak 后缀的备份文件。如果命令执行过程中出现错误,可以通过备份文件恢复原始配置。
  • /etc/yum.repos.d/[Rr]ocky*.repo:这是要修改的文件路径。文件名使用了通配符 [Rr]ocky*.repo,表示匹配 /etc/yum.repos.d/ 目录下所有以 rockyRocky 开头、以 .repo 结尾的文件。

注意,sed 命令中的 s 命令通常使用 / 字符作为分隔符,但实际上可以使用任何其他字符作为分隔符。在这种情况下,脚本使用 | 字符作为分隔符。这样做的原因是,替换的文本中包含 / 字符,那么使用 / 作为分隔符会使脚本变得混乱。

脚本寻址方式

sed 脚本的寻址方式用于指定命令作用的具体行或行范围。默认情况下,sed 命令会作用于文本的所有行。如果只想将命令作用于特定行或某些行,则必须明确指定地址范围(address)。sed 支持多种寻址方式,包括数字、正则表达式和特殊字符。

以数字形式指定行区间

当使用数字方式的行寻址时,可以用行在文本流中的行位置来引用。sed 会将文本流中的第一行编号为 1,然后继续按顺序为接下来的行分配行号。

在脚本命令中,指定的地址可以是单个行号,或是用起始行号、逗号以及结尾行号指定的一定区间范围内的行。这里举一个 sed 命令作用到指定行号的例子:

shell
[root@localhost ~]# sed '2s/小红/小绿/' example3.txt
1) 小明,23 岁,北京大学
2) 小绿,22 岁,清华大学
3) 小李,25 岁,斯坦福大学
4) 小王,22 岁,清华大学

可以看到,sed 只修改地址指定的第二行的文本。下面的例子中使用了行地址区间:

shell
[root@localhost ~]# sed '2,3s/小/大/' example3.txt
1) 小明,23 岁,北京大学
2) 大红,22 岁,清华大学
3) 大李,25 岁,斯坦福大学
4) 小王,22 岁,清华大学

在此基础上,如果想将命令作用到文本中从某行开始的所有行,可以用特殊符号 $

shell
[root@localhost ~]# sed '2,$s/小/大/' example3.txt
1) 小明,23 岁,北京大学
2) 大红,22 岁,清华大学
3) 大李,25 岁,斯坦福大学
4) 大王,22 岁,清华大学

用文本模式指定行区间

sed 允许指定文本模式来过滤出命令要作用的行,格式如下:

shell
/pattern/command

注意,必须用正斜线将要指定的 pattern 封起来,sed 会将该命令作用到包含指定文本模式的行上。这里举一个 sed 命令作用到文本的例子:

shell
[root@localhost ~]# sed '/小明/s/北京大学/中科院/' example3.txt
1) 小明,23 岁,中科院
2) 小红,22 岁,清华大学
3) 小李,25 岁,斯坦福大学
4) 小王,22 岁,清华大学

虽然使用固定文本模式能帮你过滤出特定的值,就跟上面的例子一样,但其作用难免有限,因此,sed 允许在文本模式使用正则表达式指明作用的具体行:

shell
[root@localhost ~]# sed '/22 岁/s/小/大/' example3.txt
1) 小明,23 岁,北京大学
2) 大红,22 岁,清华大学
3) 小李,25 岁,斯坦福大学
4) 大王,22 岁,清华大学

标签

sed 中,标签(label)是一种用于控制命令执行流程的机制,它允许在处理文本时根据条件跳转到指定的位置。

标签由一个冒号 : 后面紧跟一个字母数字组成的名称构成,例如 :label1:loop 等。标签通常放在 sed 脚本中的某一行开头,用于标识一个特定的位置。例如:

shell
plaintext:label1
s/old/new/

这里定义了一个名为 label1 的标签,它后面跟着一条替换命令 s/old/new/

sed 中,跳转命令是与标签配合使用的功能,用于控制命令执行的流程。这些跳转命令允许 sed 在处理文本时根据条件跳转到指定的标签位置,从而实现循环、条件分支等复杂的逻辑。

sed 提供了三种主要的跳转命令:

  • b(branch):无条件跳转到指定的标签。
  • t(test):如果上一次的替换操作(s 命令)成功,则跳转到指定的标签。
  • T(Test):如果上一次的替换操作失败,则跳转到指定的标签。

如果跳转命令中省略了标签名称,则默认跳转到脚本的末尾。

b

b 命令主要用于跳过某些操作、实现循环或提前终止处理。

shell
b [标签名]

测试文本:

text
apple pie
ignore this line
apple juice
ignore another line
banana split

跳过包含 ignore 的行,处理其他行:

shell
sed -n '
    /ignore/ b end
    s/apple/orange/ 
    p
    :end
' sed_b.txt
  • /ignore/ b end:这是一个条件判断和跳转命令。它的作用是检查当前行是否包含 ignore 这个字符串,如果包含,则使用 b 命令无条件跳转到标签 end 处,也就是跳过后面的命令,直接进入下一次循环,处理下一行。
  • s/apple/orange/:如果当前行不包含 ignore,则执行这个替换命令,将行中的 apple 替换为 orange
text
orange pie
orange juice
banana split

t

t 命令主要实现条件分支逻辑,例如循环替换或根据替换结果执行不同操作。

shell
t [标签名]
  1. 循环替换所有逗号为空格:

    shell
    echo "a,b,c" | sed '
        :loop          # 定义一个名为 "loop" 的标签
        s/,/ /         # 尝试将第一个逗号替换为空格
        t loop         # 如果替换成功,跳转回 "loop" 标签,继续处理
    '

    首先定义一个名为 loop 的标签。标签的作用是提供一个跳转目标,用于循环或条件跳转。在这里,loop 是循环的起点。s/,/ / 这是一个替换命令,尝试将模式空间中的第一个逗号(,)替换为空格( )。注意:sed 的替换命令默认只替换每行中的第一个匹配项。如果需要替换所有匹配项,需要加上 g 标志(如 s/,/ /g),但在这个例子中,通过循环来实现多次替换。

    t 是条件跳转命令,表示「如果上一次的替换操作成功,则跳转到指定的标签」。在这里,如果 s/,/ / 成功替换了逗号,sed 会跳转回 loop 标签,继续处理下一次替换。如果替换失败(即模式空间中已经没有逗号),则继续执行后续命令(在这个例子中,后续没有命令,因此直接输出结果)。

    text
    a b c
  2. 循环处理(删除行首所有连续数字)

    shell
    echo "123abc" | sed '
        :start         # 定义一个名为 "start" 的标签
        s/^[0-9]//     # 删除模式空间中的第一个数字(如果它在行首)
        t start        # 如果替换成功,跳转回 "start" 标签继续处理
    '
    text
    abc
  3. 条件分支(替换成功时打印新内容,否则打印原内容)

    shell
    sed -n '
        s/apple/orange/               # 尝试将 "apple" 替换为 "orange"
        t success                     # 如果替换成功,跳转到标签 `success`
        s/.*/[WARNING] No apple found in this line: &/  # 替换失败,生成警告信息
        p                             # 打印警告信息
        b end                         # 跳转到标签 `end`,结束当前行处理
        :success                      # 替换成功时的标签
        p                             # 打印替换后的内容
        :end                          # 结束标签
    ' sed_b.txt
    text
    orange pie
    [WARNING] No apple found in this line: ignore this line
    orange juice
    [WARNING] No apple found in this line: ignore another line
    [WARNING] No apple found in this line: banana split

T

T 命令处理未匹配到模式的情况,实现「失败分支」逻辑。

shell
T [标签名]

t 的对比:

命令触发条件用途场景
t最近的 s 替换成功时跳转替换成功后的处理(如循环替换、成功分支)
T最近的 s 替换失败时跳转替换失败后的处理(如回退、错误处理)
  1. 条件分支:替换失败时执行特定操作

    将行中的 apple 替换为 orange,若替换失败则打印警告:

    shell
    sed -n '
    s/apple/orange/  # 尝试替换
    T error          # 如果失败,跳转到 `error` 标签
    p                # 替换成功时打印
    b end            # 跳过错误处理
    :error
    s/.*/[WARNING] No apple found: &/  # 修改内容为警告信息
    p
    :end
    ' sed_b.txt
    shell
    orange pie
    [WARNING] No apple found: ignore this line
    orange juice
    [WARNING] No apple found: ignore another line
    [WARNING] No apple found: banana split
  2. 跳过未匹配的行

    仅处理包含数字的行,跳过其他行。

    shell
    sed -n '
        s/[0-9]/&/      # 尝试匹配行中的第一个数字
        T skip          # 如果没有匹配到数字,跳转到标签 "skip"
        s/.*/Found: &/p # 匹配到数字后,将整行替换为 "Found: <原始行内容>" 并打印
        :skip           # 定义跳过逻辑的标签
    ' sed_b.txt

多行命令

在深入学习 sed 命令的基础功能时,会发现一个明显的局限性:常规的 sed 命令通常仅针对单行数据进行操作。

常规的 sed 命令通常仅针对单行数据进行操作,这是因为:

  1. 逐行读取机制:sed 在读取输入数据时,会以换行符为分隔符,将文本逐行加载到模式空间(Pattern Space)中。每处理完一行,模式空间就会被清空,准备加载下一行。
  2. 模式空间的限制:模式空间默认只包含当前行的内容,无法直接感知当前行与其他行的上下文关系。因此,sed 默认只能对单行数据进行操作。
  3. 输出即刻性:处理完当前行后,sed 会立即将模式空间中的内容输出,而不是保留多行数据以供后续操作。

这种单行操作的局限性在实际文本处理场景中常常显得不足。例如,在处理跨多行的代码块、日志文件中的多行记录、或者需要匹配特定模式范围的文本时,单行操作无法满足需求。因此,sed 提供了一些特殊的机制和命令,用于实现多行操作。

为了突破单行操作的限制,sed 通过模式空间保持空间的配合,以及一些特殊的命令,实现了对多行数据的操作:

  • 模式空间(Pattern Space):这是 sed 的主要工作区域,每行输入文本会被加载到模式空间中,然后执行所有命令。默认情况下,模式空间只包含当前行。
  • 保持空间(Hold Space):这是一个额外的存储区域,用于暂存数据。它与模式空间相互独立,可以通过特定命令在两者之间传递数据。

通过模式空间和保持空间的配合,sed 可以实现多行数据的累积和操作。

同时 sed 提供了三个专门用于处理多行文本的特殊命令:

  • Next 命令(N):将数据流中的下一行加进来创建一个多行组来处理。
  • Delete(D):删除多行组中的一行。
  • Print(P):打印多行组中的一行。

N(多行操作)

N 命令的作用是将数据流中的下一行添加到当前行,从而创建一个多行组进行处理。在默认情况下,sed 一次只处理一行数据,使用 N 命令可以打破这种限制,让 sed 能够同时处理多行内容。例如,我们需要查找跨越两行的特定字符串时,就可以使用 N 命令将两行合并,以便进行匹配操作。

示例文本:

text
This is the header line.
This is the first data line. This is the second data line.
This is the last line.
text
function name1() {    
    param1,    
    param2,    
    param3
}

例如,合并包含特定单词的两行为一行:

shell
[root@localhost ~]# sed '/first/{ N ; s/\n/ / }' line_sed1.txt
This is the header line.
This is the first data line. This is the second data line.
This is the last line.

在这个例子中,sed 命令查找包含单词 first 的行。找到该行后,它会用 N 命令将下一行合并到当前行,然后用替换命令 s 将换行符 \n 替换为空格。结果是,文本文件中的两行在 sed 的输出中合并为了一行。这种操作在处理跨越多行的文本时非常有用。

还可以使用 sedN 命令和循环结构,将所有行合并为一行,并删除多余的换行符:

shell
[root@localhost ~]# sed ':a;N;$!ba;s/\n//g' line_sed2.txt
function name1() {    param1,    param2,    param3}

在这个例子中,sed 使用 :a 标签和循环结构,将文件中的所有行逐步合并到模式空间中,并最终删除所有换行符,将整个文件的内容合并为一行。

D(多行删除)

sed 不仅提供了单行删除命令(d),也提供了多行删除命令 D,其作用是只删除缓冲区中的第一行,也就是说,D 命令将缓冲区中第一个换行符(包括换行符)之前的内容删除掉。

示例文本:

text
hello
world.
hello world.
text

his is the header line.
This is a data line.

This is the last line.

可以查看下面例子:

shell
[root@localhost ~]# sed 'N ; /hello\nworld/D' line_sed3.txt
world.
hello world.

N 是把下一行加到当前行后面,中间用一个换行符 \n 分隔,经过 N 后变成 hello\nworld.,然后这里的 /hello\nworld/ 是匹配包含 hello\nworld 的内容。D 的作用是删除模式空间中直到第一个换行符 \n 为止的内容。也就是说,如果匹配到 hello\nworldD 只会删除 hello,而不会删除后面的 world

简单来说文本的第二行被 N 命令加到了缓冲区,因此 sed 命令第一次匹配就是成功,而 D 命令会将缓冲区中第一个换行符之前(也就是第一行)的数据删除,所以,得到了如上所示的结果。

如果目标是删除包含 hello 和紧接着的 world 的行,可以使用以下命令:

shell
[root@localhost ~]# sed '/hello/{N;/hello\nworld/d}' line_sed3.txt
hello world.

这里首先使用 /hello/ 找到包含 hello 的行。然后 N;把下一行加进来。然后现在的内容是 hello\nworld,就使用 d 删除整行,而不是只删除到换行符为止。

下面的例子中,它会删除数据流中出现在第一行前的空白行:

shell
[root@localhost ~]# sed '/^$/{N;/header/D}' line_sed4.txt 
his is the header line.
This is a data line.

This is the last line.

sed 会查找空白行,然后用 N 命令来将下一文本行添加到缓冲区。此时如果缓冲区的内容中含有单词 header,则 D 命令会删除缓冲区中的第一行。

P(多行打印)

sed 命令中,P(大写)命令和单行打印命令 p(小写)的功能有所不同,就如同 dD 之间存在区别一样。对于包含多行数据的缓冲区,大写 P 命令仅打印缓冲区中的第一行,即首个换行符之前的全部内容。

示例文本:

text
111
222
333
444
555
666

分别使用 p(小写)命令和 P(大写)命令处理 line_sed5.txt 文件内容后,输出信息对比如下:

shell
[root@localhost ~]# sed -n '/.*/N;p' line_sed5.txt 
111
222
333
444
555
666

由于每一行都与 /.*/ 模式匹配,所以针对每一行都会执行 N;p 命令。也就是说,对于每一行,都会把下一行添加到模式空间,接着打印模式空间的内容。

shell
[root@localhost ~]# sed -n '/.*/N;P' line_sed5.txt 
111
333
555

N 命令会将下一行添加到模式空间,而 P 命令则会打印模式空间中直至第一个换行符的内容。所以,此命令会打印出 line_sed5.txt 文件中的所有奇数行。

具体来说:

  1. 处理第一行 111 时,N 命令会把下一行 222 添加到模式空间,此时模式空间内容为 111\n222 。随后执行 P 命令,会打印模式空间中直到第一个换行符的内容,即 111
  2. 处理第二行 222 时,因为上一步已将其添加到模式空间,所以这一步不会再次添加。因此,执行 P 命令时不会输出任何内容。
  3. 处理第三行 333 时,同样会将下一行 444 添加到模式空间,然后打印模式空间中直到第一个换行符的内容,即 333
  4. 依此类推,最终打印结果为所有奇数行。

空间

在使用 sed 进行文本处理时,它提供了两个重要的空间来存储和操作文本,分别是模式空间(pattern space)和保留空间(hold space)。这两个空间在 sed 的文本处理流程中扮演着不同但又相互协作的角色。

模式空间

模式空间是 sed 处理每一行文本时的主要临时缓冲区。当 sed 开始处理文本文件时,它会逐行读取文件内容,并将每一行文本依次放入模式空间中进行处理。模式空间就像是一条流水线,文本行在其中接受各种 sed 命令的操作,如插入、删除、替换和转换等。

在默认情况下,如果没有使用 -n 选项,模式空间中的内容在处理完成后会主动打印到标准输出,并且模式空间会自动清空,以便处理下一行文本。

模式空间特点:

  • 模式空间的内容会在每次处理完后自动清空,并加载下一行。
  • 默认情况下,模式空间中的内容会被打印到标准输出。
  • 模式空间可以被修改、替换或删除,例如通过 s(替换)、d(删除)等命令。

模式空间是 sed 的主要工作区域,用于对当前行进行编辑和处理。

保留空间

保留空间是 sed 提供的一个辅助临时缓冲区,类似于一个仓库。它用于存放稍后可能需要使用的文本,在模式空间对数据进行处理时,可以把部分或全部数据临时存储到保留空间中,以备后续操作使用。

与模式空间不同的是,保留空间的内容不会主动清空,也不会主动打印到标准输出,需要通过特定的 sed 命令来进行操作。需要注意的是,sed 命令通常是对模式空间进行操作,不能直接寻址保留空间,但可以通过上述的复制和交换命令,在模式空间和保留空间之间进行数据的交互,从而实现更复杂的文本处理需求。

保留空间特点:

  • 保留空间的内容不会自动清空,也不会自动打印到标准输出。
  • 它是一个独立的缓冲区,可以存储模式空间的内容,也可以将保留空间的内容复制回模式空间。
  • 保留空间通常用于多行操作或需要暂存数据的场景。

保留空间相当于一个「仓库」,用于临时存储模式空间中的内容,以便在后续的处理中使用。

空间的交互

sed 提供了一组命令来在模式空间和保留空间之间移动文本。这些命令包括:

空间相关描述
h将模式空间的内容复制到保持空间。它会覆盖保持空间中的任何现有文本。
H将模式空间的内容追加到保持空间。它会在保持空间中的现有文本后面添加一个换行符,然后追加模式空间的内容。
g将保持空间的内容复制到模式空间。它会覆盖模式空间中的任何现有文本。
G将保持空间的内容追加到模式空间。它会在模式空间中的现有文本后面添加一个换行符,然后追加保持空间的内容。
x将模式空间中的内容复制到保留空间,同时将保留空间中的内容复制到模式空间。

通常,在使用 hH 命令将字符串移动到保持空间后,最终还要用 gGx 命令将保存的字符串移回模式空间。保持空间最直接的作用是,一旦将模式空间中所有的文件复制到保持空间中,就可以清空模式空间来加载其他要处理的文本内容。

由于有两个缓冲区域,下面的例子中演示了如何用 hg 命令来将数据在 sed 缓冲区之间移动:

示例文本:

text
春有百花秋有月,
夏有凉风冬有雪。
若无闲事挂心头,
便是人间好时节。
shell
[root@localhost ~]# sed -n '/凉风/ {h ; p ; n ; p ; g ; p }' line_sed6.txt
夏有凉风冬有雪。
若无闲事挂心头,
夏有凉风冬有雪。

对于 line_sed6.txt 文件中的每一行,都会检查是否匹配 /凉风/。如果匹配,则执行大括号内的命令。

在例子中,只有第二行 夏有凉风冬有雪。 匹配该模式。因此,对于这一行,会执行以下命令:

  1. h:将模式空间的内容(即 夏有凉风冬有雪。)复制到保留空间。
  2. p:打印模式空间的内容(即 夏有凉风冬有雪。)。
  3. n:读取下一行(即 若无闲事挂心头,)并将其添加到模式空间。
  4. p:打印模式空间的内容(即 若无闲事挂心头,)。
  5. g:将保留空间的内容(即 夏有凉风冬有雪。)复制到模式空间。
  6. p:打印模式空间的内容(即 夏有凉风冬有雪。)。