sed 是一个轻量级 stream 流编辑器。它可以对文件和输入流(例如管道)执行基本的文本操作。

您可以使用 sed 进行搜索,查找和替换,插入和删除字符串和行。它支持基本和扩展的正则表达式,使您可以匹配复杂的模式。

在本教程中,我们将讨论如何在 Linux 使用 sed 查找和替换字符串。我们还将向您展示如何执行递归搜索和替换。

sed 搜索并替换字符串

sed 有多个版本,它们之间在功能上有所不同。MacOS 使用 BSD 版本,并且大多数 Linux 发行版均默认预装 GNU sed。我们将使用 GNU 版本。

使用 sed 搜索和替换文本采用采用以下语法形式:

sed -i 's/SEARCH_REGEX/REPLACEMENT/g' INPUTFILE
  • -i 选项指示 sed 直接编辑文件。默认情况下,sed 将其输出写入到标准输出。如果提供扩展名(例如-i.bak),sed 会创建原始文件的备份。
  • s 是替换命令,是 sed 最常用的命令。
  • / 分隔符。它可以是任意字符,但通常使用斜杠 / 作为分隔符。
  • SEARCH_REGEX 要搜索的字符串或正则表达式。
  • REPLACEMENT 使用此字符串进行替换。
  • g 全局替换标志。默认情况下,sed 逐行读取文件,并且仅修改文件行中与搜索模式匹配第一次出现的字符。提供 g 替换标志后,所有匹配项都将被替换。
  • INPUTFILE 要执行替换,删除,编辑等操作文件的名称。

sed 命令最佳实践是表达式周围加上引号,转义 Shell 元字符,避免 Shell 的解释 这里的表达式指的是 's/SEARCH_REGEX/REPLACEMENT/g'

让我们看一下如何在 Linux 使用 sed 命令搜索和替换文件中字符串或者文本。出于演示目的,我们将使用 file.txt 文件:

123 Foo foo foo 
foo /bin/bash Ubuntu foobar 456
file.txt

如果省略 g 标志,则仅替换每行与搜索模式匹配第一个字符串。sed -i 's/foo/linux/' file.txt 命令将会使用 linux 替换 foo:

sed -i 's/foo/linux/' file.txt
123 Foo linux foo 
linux /bin/bash Ubuntu foobar 456

当使用全局替换标志 g, sed 替换所有与搜索模式匹配的字符串:

sed -i 's/foo/linux/g' file.txt
123 Foo linux linux
linux /bin/bash Ubuntu linuxbar 456

您可能已经注意到,在前面的示例中,字符串 foobar 内的子字符串 foo 也被替换了。

如果这不是你想要的行为,请在搜索字符串的两端使用单词边界表达式 \b。这样可以确保单词的部分不被匹配。

sed -i 's/\bfoo\b/linux/g' file.txt
123 Foo linux linux
linux /bin/bash Ubuntu foobar 456

要使搜索模式的匹配不区分大小写,请使用 sed 的 I 选项。在下面的示例中,我们同时使用 gI 选项进行搜索替换:

sed -i 's/foo/linux/gI' file.txt
123 linux linux linux 
linux /bin/bash Ubuntu linuxbar 456

如果你要搜索替换的字符串包含分隔符 /,则需要使用反斜杠 \ 来转义斜杠 /。例如替换 /bin/bash/usr/bin/zsh,请运行以下命令:

sed -i 's/\/bin\/bash/\/usr\/bin\/zsh/g' file.txt

更简单易读的选项是使用另一个字符作为分隔符。大多数人使用竖线 | 或冒号 : 作为分隔符,但也您可以使用你喜欢的任意字符:

sed -i 's|/bin/bash|/usr/bin/zsh|g' file.txt
123 Foo foo foo 
foo /usr/bin/zsh Ubuntu foobar 456

您也可以使用正则表达式。例如,搜索连续的 3 个数字并将其替换为 number,请运行以下命令:

sed -i 's/\b[0-9]\{3\}\b/number/g' file.txt
number Foo foo foo 
foo /bin/bash demo foobar number

sed 的另一个非常有用功能是您可以使用 & 符号记住搜索模式匹配的值,然后在后面引用它。

你可以多次使用 & 符号。例如,如果要在与搜索模式匹配的 3 个数字周围添加花括号 {},请运行以下命令:

sed -i 's/\b[0-9]\{3\}\b/{&}/g' file.txt
{123} Foo foo foo 
foo /bin/bash demo foobar {456}

最后但并非最不重要的一点是,使用 sed 编辑文件时,先进行备份是一个良好习惯。要备份原始文件,只需在 sed 命令的 -i 选项指定备份文件的后缀名即可。

例如,要编辑 file.txt 并备份原始文件名为 file.txt.bak 。请运行以下命令:

sed -i.bak 's/foo/linux/g' file.txt

如果要确保是否已创建原始文件的备份,请运行 ls 命令列出文件:

ls
file.txt file.txt.bak

递归查找和替换

有时您想在目录中进行递归搜索包含指定字符串的文件,并且使用指定的字符串替换所有文件。

可以通过 find 命令遍历目录的所有文件,然后通过 -exec 选项运行 sed 命令来完成对文件中字符串的搜索与替换。

以下命令将递归遍历当前工作目录的所有文件,并将文件名传递给 sed 将 bar 全局替换 为 foo。

find . -type f -exec sed -i 's/foo/bar/g' {} +

为避免文件名中包含空格的问题,请使用 find 命令 -print0 选项,该选项指示 find 命令后跟一个空字符打印文件名。

然后将 find 命令的标准输出通过管道传递给 xargs 命令。xargs 命令将会为 sed 命令提供最后一个参数文件名。

此处的 xargs 命令的 -0 选项是告诉 xargs 命令,传递给 xargs 命令的参数没有使用任何分隔符分隔参数,因为 find 使用了 -print0 选项。

因此最终的命令是 find . -type f -print0 | xargs -0 sed -i 's/foo/bar/g'。

find . -type f -print0 | xargs -0 sed -i 's/foo/bar/g'

要排除目录,请使用find 命令 -not -path 选项。例如,如果要替换本地 git 仓库以点 . 开头的文件,请运行以下命令:

find . -type f -not -path '*/\.*' -print0 | xargs -0 sed -i 's/foo/bar/g'

如果只是想搜索并替换具有指定扩展名的文件的内容,则可以运行以下命令:

find . -type f -name "*.md" -print0 | xargs -0 sed -i 's/foo/bar/g'

另一种选择是使用 grep 命令以递归方式搜索包含搜索模式的所有文件,然后将文件名通过管道传递给 sed 命令对文件内容进行搜索替换:

grep -rlZ 'foo' . | xargs -0 sed -i.bak 's/foo/bar/g'

结论

尽管看似复杂,但一开始,使用 sed 搜索和替换文件中的文本非常简单。要了解 sed 命令,选项和标志的更多信息。

请访问 GNU sed手册Grymoire sed 教程。如果您有任何问题或反馈,请随时发表评论。