Linux Sed 文本编辑完全指南:语法与实例详解

本文详细介绍了Linux Sed流编辑器的核心概念、语法结构和实用技巧,涵盖文本替换、删除、插入、模式匹配等操作,并提供了Dockerfile和Shell脚本中的实际应用示例,帮助读者掌握高效的非交互式文本处理技能。

Linux Sed 教程:学习文本编辑语法与实例

Sed 简介

Sed 是“流编辑器”的缩写。流指的是字节的源或目的地。换句话说,sed 可以从标准输入(stdin)读取输入,对流应用指定的编辑,并自动将结果输出到标准输出(stdout)。Sed 语法允许在命令行上指定输入文件。然而,该语法不直接支持输出文件规范;这可以通过输出重定向或编辑文件并可选地备份原始副本来实现。

Sed 是 Linux 和类 Unix 系统上最强大的工具之一。学习它是值得的,因此在本教程中,我们将从 sed 命令语法和示例开始。

教程要求

要求:Linux
Root 权限:否
难度级别:简单
类别:文本处理
OS 兼容性:BSD • Linux • macOS • Unix
预计阅读时间:15 分钟

目录

  1. Sed 工作原理
  2. 语法
  3. 编辑命令
  4. 寻址
  5. 示例
  6. 打印
  7. 替换(查找和替换)
  8. 读取文件
  9. 删除行
  10. 插入/追加文本
  11. 创建 Sed 脚本
  12. 写入命令
  13. 在 Dockerfile 中使用 Sed
  14. 在 Shell 脚本中使用 Sed
  15. 总结

使用 Sed 编辑器执行非交互式编辑

Sed 是一个流编辑器。对于交互式文本编辑,您可以使用 vi/vim、nano 或 emacs 等编辑器。但是,sed 适用于在命令行界面(CLI)中在脚本或 Dockerfile 中进行非交互式文件编辑。

默认情况下,sed 以非破坏性方式操作。您需要指定输出文件来保存更改,或使用特殊的 GNU sed 选项来就地编辑文件。

它提供正则表达式(regex)以实现强大的文本操作。

Sed 工作原理

Sed 逐行工作。它将每一行读入模式缓冲区,通过 sed 命令修改该行,然后将缓冲区输出到标准输出(stdout),可以重定向到另一个文件。默认情况下,原始文件不会被修改。

Sed 维护两个数据缓冲区

Sed 命令维护两个数据缓冲区。两者最初都是空的:

  • 模式缓冲区(活动模式空间):当 sed 从输入中逐行读取时,它会将该行放入模式空间。这是进行文本操作的地方。例如,您可以使用 s 进行替换、d 进行删除、p 进行打印等 sed 命令。默认情况下,模式空间在每行读取周期结束时清除。
  • 保持缓冲区(辅助保持空间):顾名思义,保持缓冲区充当保持空间。它是一个辅助缓冲区,sed 用于临时存储。可以将其视为一个保存数据的地方,以便在处理不同行时稍后使用。您可以使用它进行高级操作,如复制、追加、比较或检索命令。保持缓冲区的典型用法是在排序的输入文件中查找重复行或将多行连接在一起进行高级编辑。与模式空间不同,保持空间在周期之间保留其内容,除非您明确更改它。换句话说,这允许您跨多行存储和召回信息。您使用特定的 sed 命令(h、H、g、G、x)在模式空间和保持空间之间移动数据。

简而言之,模式空间是立即进行编辑的地方,而保持空间提供了一种为更复杂的编辑任务保存和召回信息的方式。标准输入(stdin)通常是键盘、文件或另一个数据流。标准输出(stdout)通常是屏幕或文件。

GNU Linux Sed 命令语法

通常 GNU 版本的 sed 运行如下:

1
2
3
sed 'commands' input_file 
sed 'commands' input_file > output_file
sed 'commands' input_file | command2

更准确的语法:

1
2
sed [options] '[addresses] action [args]' input_files [ > outfile]
sed [options] '[addresses] action [args]' input_files [ | command_2]

您无需在运行时与 sed 编辑器交互;因此,它也被称为批处理编辑器。这与 Vim (vi)、emacs、nano 和 ed 等交互式编辑器形成对比。由于 sed 不需要交互,您可以将 sed 命令放在脚本中。您可以调用脚本文件并针对数据文件运行它以执行重复的编辑操作:

1
sed SCRIPT input_file

GNU Sed 编辑命令

最有用的 sed 命令灵感来自 vi (vim) 和 ed,99% 的用户大量使用它们:

表 1:Sed 命令

命令 描述
d 删除行
p 打印行
i 插入行
r 读取文件
s 用一个字符串替换另一个(在文件中查找和替换文本)
w 写入文件

除此之外,GNU/sed 命令还有一些有用的 CLI 选项:

表 2:GNU/sed CLI 选项

CLI 选项 描述
-n 抑制模式空间的自动打印,即默认输出
-f script 从脚本文件读取 sed 命令
-i {BACKUP} 就地编辑文件。这对于 Dockerfile 和其他此类用法非常有用
–posix 禁用 sed 的所有 GNU 扩展。当您为 Unix、macOS、*BSD 和 Linux 编写 sed 脚本时,这很有用
-E 或 -r 在脚本中使用扩展正则表达式

Sed 寻址

在我们看实际示例之前,您需要理解的最后一件事是 sed 寻址,它说明了如何指定输入文件的哪些行应受 sed 命令影响。除非您指定地址,否则 sed 编辑器会处理所有输入文件行。此地址可以是一系列行号、正则表达式或两者的组合。如果您不提供任何地址,sed 命令将应用于输入的每一行。

地址类型:

  • 行号 - 您可以指定特定的行号(例如,42)以定位该行。您可以使用 $ 表示输入的最后一行,或者当在正则表达式中使用时,$ 字符表示行尾(EOL)。
  • 正则表达式(regex) - 您可以使用正则表达式(例如,/pattern/)来选择匹配特定模式的行。简而言之,只有包含该模式的行会被编辑。
  • 地址范围 - 您可以使用行号和/或正则表达式的组合来指定行范围,用逗号分隔(例如,100,200/word1/,/word2/)。

示例

考虑以下 data.txt 文件,这里是标题信息:

1
NAME|DOB|Location|Job Title|Salary $

使用 cat 命令或 bat 命令显示的随机样本数据:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
John Doe|1985-03-15|New York|Software Engineer|80000
Aarav Patel|1990-06-21|Mumbai|Data Analyst|88000
Jane Smith|1992-11-20|London|Data Scientist|95000
David Lee|1978-07-08|Tokyo|Project Manager|110000
Thandiwe Zulu|1993-04-03|Cape Town|Business Analyst|93000
Li Wei|2002-07-25|Shanghai|AI Researcher|86000
Priya Sharma|1987-01-14|Delhi|Software Tester|79000
Sipho Nkosi|1976-11-29|Johannesburg|IT Manager|102000
Sarah Jones|2001-05-02|Paris|Web Developer|75000
Michael Brown|1969-12-25|Sydney|System Administrator|90000
Emily Davis|1998-09-10|Berlin|UX Designer|85000
Kevin Wilson|1975-04-30|Toronto|Database Admin|100000
Jessica Garcia|2003-01-18|Rome|QA Tester|70000
Kenji Kimura|1973-02-07|Kyoto|Systems Engineer|99000
Brian Rodriguez|1982-08-22|Madrid|Network Engineer|92000
Ashley Williams|1995-06-05|Amsterdam|Frontend Developer|78000
Christopher Martinez|1972-10-12|Vienna|Security Analyst|98000
Amanda Anderson|2000-02-28|Dublin|Mobile Developer|82000
Matthew Thomas|1988-09-01|Stockholm|Cloud Architect|105000
Elizabeth Jackson|1979-11-17|Helsinki|DevOps Engineer|97000
Daniel White|2004-03-09|Copenhagen|Junior Developer|68000
Zhang Lei|1984-05-11|Beijing|Cybersecurity Expert|94000

使用 Sed 打印(p 命令)文本文件数据

以下示例说明如何使用 p(打印)命令,该命令将一系列行打印到 stdout。范围由起始地址后跟逗号和结束地址指定。例如,尝试打印第 5 到第 8 行:

1
$ sed '5,8p' data.txt

sed 的默认输出是它读取的每一行。要隐藏或抑制默认输出,请使用 -n 选项:

1
$ sed -n '5,8p' data.txt

以下命令打印所有包含模式 Software 的行,即所有包含单词 ‘Software’ 的匹配行。使用正斜杠 (/) 分隔正则表达式:

1
$ sed -n '/Software/p' data.txt

输出:

1
2
John Doe|1985-03-15|New York|Software Engineer|80000
Priya Sharma|1987-01-14|Delhi|Software Tester|79000

正则表达式后的 I 标志使其不区分大小写模式:

1
$ sed -n '/software/Ip' data.txt

以下 sed 命令打印包含模式 David 的第一行,直到并包括包含模式 Emily 的下一行,即打印两个匹配单词或模式之间的行:

1
$ sed -n '/David/,/Emily/p' data.txt

以下 sed 命令显示包含模式 Ashley 的第一行,直到文件的最后一行,使用 $ 作为输入的最后一行:

1
$ sed -n '/Ashley/,$p' data.txt

在此示例中,将上述 sed 命令输出保存到当前目录中名为 ‘output.txt’ 的文本文件:

1
$ sed -n '/Ashley/,$p' data.txt > output.txt

验证:

1
$ cat output.txt

请注意,模式可能包含 grep 命令使用的正则表达式字符。

使用 Sed 替换文本(使用 s 命令查找和替换)

Sed s 命令允许对文本进行搜索和替换操作。换句话说,您可以找到给定的"单词"并用"新单词"替换它。该命令使用模式搜索和字面字符串替换,并完成元字符扩展。假设在 /etc/passwd 中找到单词 vivek 并将其替换为名为 mr_vivek 的单词:

1
$ sed 's/vivek/mr_vivek/' /etc/passwd

让我们找到单词 Software 并替换为 SOFTWARE_JOB:

1
$ sed 's/Software/SOFTWARE_JOB/' data.txt

关于保存 Sed 命令文本操作的说明

有两个选项。第一个选项是将 sed 命令的文本操作结果保存到文件,您可以使用输出重定向如下:

1
$ sed 'command' INPUT > OUTPUT

> 符号将标准输出(stdout)重定向到文件。例如:

1
$ sed 's/Software/SOFTWARE_JOB/' data.txt > output.txt

如果要追加输出到现有文件而不是覆盖它,请使用 >> 符号:

1
$ sed 's/Software/SOFTWARE_JOB/' data.txt >> output.txt

第二个选项适用于 GNU/sed(大多数 Linux 发行版上的 sed 默认版本),您可以使用 -i 选项直接就地编辑文件。这避免了重定向和覆盖文件的需要。但是,请小心,因为它会修改原始文件。这对于脚本和 Dockerfile 非常有用:

1
$ sed -i 's/old_word/new_word/' file.txt

在使用 -i 选项时,通常最好创建备份,如下所示:

1
$ sed -i'BAK' 's/old_word/new_word/' file.txt

例如:

1
2
3
$ cp -v data.txt file.txt
$ ls -l file*
$ sed -i'.BAK' 's/Software/SOFTWARE_JOB/' file.txt

验证:

1
2
$ ls -l file*
$ diff file.txt file.txt.BAK

以下 sed 命令示例显示了带有 s(搜索和替换)命令的 g(全局)命令标志,它将所有出现的 ‘old’ 单词/字符串替换为 ’new’ 字符串或单词:

1
2
$ sed 's/old/new/g' input.txt
$ sed 's/Software/SOFTWARE_JOB/g' data.txt

I 标志使搜索和替换(s 命令)不区分大小写模式:

1
$ sed 's/software/SOFTWARE_JOB/Ig' data.txt

有时,在执行搜索和替换时,旧字符串可能包含在新的替换字符串中。您可以通过在替换字符串中放置与号(&)来实现这一点。与号的位置将决定旧字符串在新字符串中出现的位置。语法是:

1
$ sed 's/old/& new/g' input

换句话说,sed s(替换)命令的替换字符串中的 & 表示模式的整个匹配部分。这对于在匹配模式周围或内部添加文本而无需显式重复模式本身非常有用。这里我在名为 ‘Vivek’ 的单词或模式周围添加 * 符号:

1
2
3
$ echo 'Hello, Vivek'
$ echo 'Hello, Vivek' | sed 's/Vivek/*&*/'
Hello, *Vivek*

在此示例中,我为数字 1000 添加前缀 $:

1
$ echo 'The price is 1000 for new MacBook air.' | sed 's/1000/\$&/'

输出:

1
The price is $1000 for new MacBook air.

正如我所写,$ 有特殊含义:“行尾”。如果要在 sed 中匹配字面美元符号字符,需要对其进行转义。反斜杠 () 告诉 sed 将 $ 视为常规字符,而不是其特殊的"行尾"元字符。因此 sed 命令将找到 ‘old’ 并使用 ‘&’ 将其替换为 ‘old new’。让我们尝试使用 egrep 命令打印薪水列:

1
$ grep --color -E '([0-9]+)$' data.txt

现在,我想将每个薪水数字(如 80000)替换为 $80000:

1
$ sed -E 's/([0-9]+)$/\$&/g'  data.txt

其中:

  • sed 命令从文件 data.txt 读取每一行(sed ... data.txt)。
  • 它匹配薪水(([0-9]+)$):它识别行尾的数字序列(代表薪水)并使用扩展正则表达式存储它。
  • 然后它添加美元符号(\$&):它在匹配的薪水前插入美元符号($)。
  • 输出修改后的行:它将修改后的行打印到屏幕/stdout。

在 Sed 命令中使用 Shell 变量

在 sed 命令中使用 shell 变量可以为文本处理添加动态行为。必须将 sed 命令用双引号(")括起来,以允许 shell 在命令内扩展变量。例如:

1
2
o_value=20000
sed -i'.BAK' "s/10000/$o_value/g" php.conf

此 sed 命令将所有出现的 “10000” 替换为 $o_value 变量的值,即 “20000”。如果使用单引号(’),变量将不会扩展,sed 将尝试匹配字面字符串 “$o_value”。您可以使用命令替换动态生成替换文本。例如:

1
$ sed "s/current_directory/$(pwd)/g" file1.txt

此 sed 命令将 “current_directory” 替换为 pwd 命令的输出。

使用不同的字符作为分隔符

默认情况下,替换命令中的分隔符是 /。您可以更改分隔符:

1
2
3
4
5
# 默认分隔符是 '/'
sed 's/OLD/NEW/' input_file

# 将分隔符设置为 '_'
sed 's_OLD_NEW_' input_file

当 shell 变量包含分隔符本身时,这很有用。例如:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# 变量包含 sed s 命令中使用的分隔符
bak_path="/efs/www_static_cache"

# 这将失败,因为 $bak_path 包含 '/'
sed "s/old_path/$bak_path/g" aws.nfs.config

# 要解决此问题,请将分隔符更改为其他内容
sed "s+old_path+$bak_path+g" aws.nfs.config
# 或者 #
sed "s_old_path_$bak_path_g" aws.nfs.config

使用 r 命令从文件读取新文本

Sed 中的 “r” 命令代表"读取"。它允许您读取指定文件的内容,并将这些内容追加到当前模式空间,即匹配行之后正在处理的行。换句话说,您可能希望多次重复该过程,无论是在同一文件中还是跨多个文件。“r”(读取)命令指定一个文件名,文件的内容在地址指定的行之后插入到输出中。地址可以是行号或模式组合。例如,您有 foo.txt 和 bar.txt。您想在 foo.txt 中包含单词 “Unix.” 的行之后插入 bar.txt 的内容。这是 sed 命令:

1
2
$ ls -l bar.txt foo.txt
$ cat bar.txt

输出:

1
2
3
**
In Linux, every problem is solvable, and every solution is a new adventure.
**

其他文件:

1
$ cat foo.txt

输出:

1
2
3
4
Unix is basically a simple operating system, but you have to be a genius to understand the simplicity.
FreeBSD is very nice.
I like macOS.
Debian is very nice for server.

这是 sed 命令:

1
$ sed '/Unix/r bar.txt' foo.txt

使用 Sed 删除文本

以下命令从文件中删除第 8 到第 12 行:

1
$ sed '8,12d' data.txt

删除特定行 #42:

1
$ sed '42d' input.txt

删除第 13 行并使用 -i 选项修改 input.txt:

1
2
$ sed -i '13d' input.txt
$ cat input.txt

以下命令删除任何包含模式 ‘Mumbai’ 的行:

1
$ sed  '/Mumbai/d' data.txt

正则表达式后的 I 标志使其不区分大小写模式,换句话说匹配 MUMBAI、Mumbai、mumbai 等所有:

1
$ sed  '/mumbai/Id' data.txt

也可以删除所有空行:

1
$ sed '/^$/d' my_file.txt

使用 printf 命令并删除所有空行的另一个示例:

1
2
$ printf "%s\n\n\n%s\n" "This is a test" "Last line"
$ printf "%s\n\n\n%s\n" "This is a test" "Last line" | sed '/^$/d'

以下命令删除任何以模式 ‘Linux’ 开头的行:

1
$ sed '/^Linux/d' input.txt

以下 sed 命令删除从包含模式 FOO 的第一行开始,直到文件中包含 BAR 的下一行的行范围:

1
$ sed '/FOO/,/BAR/d' filename.txt

以下是删除每行前三个字符的方法:

1
2
$ sed 's/^...//' input.txt
$ sed 's/^...//' input.txt > output.txt

其中流编辑器 sed 的使用如下。主要魔力在于 's/...//',这是替换命令:

  • s/ - 表示替换命令的开始。
  • ^ - 匹配行的开头。
  • ... - 匹配任何三个字符。.(点)匹配任何单个字符。
  • // - 用空替换匹配的三个字符,有效地删除它们。
  • input.txt - 指定输入文件。
  • > output.txt - 指定输出文件。

使用 Sed 插入和追加文本

i 命令用于在指定行之前插入一行。例如:

1
$ sed '2i\FOO line will be inserted before line 2.' data.txt

以下是插入多行的方法:

1
2
3
$ sed '5i\
FOO line\
BAR line' data.txt

反斜杠转义换行符,允许您在下一行写入插入的行。您还可以基于模式匹配插入行。例如:

1
2
$ sed '/Mumbai/i\
line will be inserted before the line containing "Mumbai".' data.txt

a 命令用于在指定行之后插入一行:

例如:

1
2
3
4
5
$ sed '5a\
This line will be inserted after line 5.' input.txt
$ sed '5a\
FOO line\
BAR line' input.txt

同样,您可以在模式匹配之后插入行:

1
2
$ sed '/Toronto/a\
Line will be inserted after the line containing "Toronto".' data.txt

从文件读取 Sed 命令

使用文件存储 sed 命令对于复杂的编辑任务非常有用。sed 中的 -f 选项允许您指定包含 sed 命令的文件。语法是:

1
$ sed -f script.sed input_file.txt

可以将多个 sed 命令放入名为 ‘script.sed’ 的文件中,并使用 -f 选项执行。当您将命令放在文件中时:

  • 不要在操作和地址周围使用引号。
  • 确保每行末尾没有尾随空格。

让我们创建一个 script.sed,其中包含以下命令:

  • 删除前两行。
  • 将任何 Software 实例替换为 Software_JOB
  • 将以 Emily 开头的任何行更改为 Emilia
1
$ cat script.sed

输出:

1
2
3
1,2d
s/Software/Software_JOB/
s/^Emily/Emilia/

运行如下:

1
$ sed -f script.sed data.txt

当然,您可以保存或就地更新文件,如下所示:

1
2
3
$ sed -i -f sed_commands.txt input.txt
## 或者 ##
$ sed -f sed_commands.txt input.txt > output.txt

如何执行多个 Sed 命令

当您想从命令行执行多个 sed 命令时,请尝试以下语法:

1
2
$ sed -e 'command1' -e 'command2' input_file
$ sed -e 'command1' -e 'command2' input_file > output_file

在此示例中,您使用 -e CLI 选项替换多个模式:

1
$ sed -e 's/BSD/macOS/g' -e 's/Unix/Linux/g' input.file > output.file

此命令将 input.file 中所有出现的 “BSD” 替换为 “macOS”,然后将所有出现的 “Unix” 替换为 “Linux”,并将输出存储到 output.file。

使用 Sed 写入输出文件

Sed 命令本身有一个 w 命令,允许您将特定行或模式写入文件。例如:

1
$ sed '/pattern/w output.txt' input.txt

w 命令允许特定的 sed 命令将输出写入给定的文件名。不同的 sed 命令可以写入不同的文件。例如:

1
$ cat demo.sed

输出:

1
2
/Delhi|Mumbai/w india.office.txt
s/^Emily/Emilia/w emily.typo.txt

运行如下:

1
2
3
4
$ sed -E -n demo.sed data.txt
$ ls -l *.txt
$ cat india.office.txt
$ cat emily.typo.txt

如何在 Dockerfile 中使用 Sed

Sed 语法与 CLI 相同,假设您想在构建容器和应用程序时在 /etc/env.conf 中将 10000 编辑为 20000,您需要在 Dockerfile 中添加以下内容:

1
RUN sed -i'.BAK' 's/^10000/20000/'  /etc/env.conf

Dockerfile 中的 RUN 指令主要在构建过程中在 Docker 镜像内执行命令。它用于安装软件、配置设置以及使用 sed、awk 和其他工具准备镜像的任何其他必要操作。这是 Dockerfile 的另一个示例,首先设置一些变量,然后更新文件:

1
2
3
4
5
ENV LC_ALL=en_US.UTF-8
ENV LANG=en_US.UTF-8
ENV LANGUAGE=en_US.UTF-8
RUN sed -i "s/^# $LANG/$LANG/" /etc/locale.gen; \
    locale-gen

在 Shell 脚本中使用 Sed

您可以简单地调用 sed 命令。语法是:

1
2
3
4
5
6
7
#!/usr/bin/evn bash
echo "Starting setup ..."
# 调用 sed 编辑配置文件
sed -i'.BAK' 'command' some_config.file
# 示例:
sed -i'.factory' -e 's/;Interface ""/Interface "eth0"/g' /etc/vnstat.conf
echo "Setup done..."

在此示例中,我使用 sed 命令编辑 php fpm Web 服务器文件以配置它:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#!/bin/bash
set -e
profile="$1"

if [ -f "$profile" ]
then
    echo "*** Using $profile file ..."
    source "$profile"
    # 配置 PHP
    sed -i'.factory' -e "s+listen = 127.0.0.1:9000+listen = ${php_fpm_sock_path}+" \
        -e 's/user = nobody/user = nginx/' \
        -e 's/group = nobody/group = nginx/' \
        -e 's/;listen.owner = nobody/listen.owner = nginx/' \
        -e 's/;listen.group = nobody/listen.group = nginx/' \
        -e 's/;rlimit_files = 1024/rlimit_files = 655350/' \
        -e 's/pm.max_children = 5/pm.max_children = 300/' \
        -e 's/pm.start_servers = 2/pm.start_servers = 100/' \
        -e 's/pm.min_spare_servers = 1/pm.min_spare_servers = 100/' \
        -e 's/pm.max_spare_servers = 3/pm.max_spare_servers = 200/' \
        -e 's/;pm.max_requests = 500/pm.max_requests = 500/' "$php_fpm_www_conf"
else
    echo "Error - $0 - '$profile' profile file not found. Set correct profile file."
    exit 1
fi

总结

这结束了我们在 Linux 中使用 sed 的教程。我强烈建议在线阅读 GNU sed 文档或键入以下 info 命令/man 命令命令:

1
$ man sed

下次,我将介绍 sed 保持空间教程。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计