在Linux中使用SedStreamEditor处理文本的基础知识

来自菜鸟教程
跳转至:导航、​搜索

介绍

sed 命令是 stream editor 的缩写,对来自标准输入或文件的文本执行编辑操作。 sed 以非交互方式逐行编辑。

这意味着您在调用命令时做出所有编辑决定,并且 sed 自动执行指令。 这可能看起来令人困惑或不直观,但它是一种非常强大且快速的文本转换方式,尤其是作为脚本或自动化工作流程的一部分。

本教程将介绍一些基本操作,并向您介绍操作此编辑器所需的语法。 您几乎可以肯定永远不会用 sed 替换您的常规文本编辑器,但它可能会成为您的文本编辑工具箱中受欢迎的补充。

注意:本教程使用在 Ubuntu 和其他 Linux 操作系统上找到的 sed 的 GNU 版本。 如果您使用的是 macOS,您将拥有具有不同选项和参数的 BSD 版本。 您可以使用 brew install gnu-sed 安装带有 Homebrewsed 的 GNU 版本。


启动交互式终端!

基本用法

sed 对从文本文件或标准输入 (STDIN) 读取的文本流进行操作。 这意味着您可以将另一个命令的输出直接发送到 sed 进行编辑,或者您可以处理您已经创建的文件。

您还应该知道 sed 默认情况下将所有内容输出到标准输出 (STDOUT)。 这意味着,除非重定向,否则 sed 会将其输出打印到屏幕上,而不是将其保存在文件中。

基本用法是:

sed [options] commands [file-to-edit]

在本教程中,您将使用 BSD 软件许可证 的副本来试验 sed。 在 Ubuntu 上,执行以下命令将 BSD 许可证文件复制到您的主目录,以便您可以使用它:

cd
cp /usr/share/common-licenses/BSD .

如果您没有 BSD 许可证的本地副本,请使用以下命令自己创建一个:

cat << 'EOF' > BSD
Copyright (c) The Regents of the University of California.
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
   notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
   notice, this list of conditions and the following disclaimer in the
   documentation and/or other materials provided with the distribution.
3. Neither the name of the University nor the names of its contributors
   may be used to endorse or promote products derived from this software
   without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
EOF

让我们使用 sed 来查看 BSD 许可证文件的内容。 sed 默认将其结果发送到屏幕,这意味着您可以通过不传递任何编辑命令将其用作文件阅读器。 尝试执行以下命令:

sed '' BSD

您将看到 BSD 许可证显示在屏幕上:

OutputCopyright (c) The Regents of the University of California.
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
    notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
    notice, this list of conditions and the following disclaimer in the
    documentation and/or other materials provided with the distribution.
...
...

单引号包含您传递给 sed 的编辑命令。 在这种情况下,您什么也没传递,因此 sed 将收到的每一行打印到标准输出。

sed 可以使用标准输入而不是文件。 将 cat 命令的输出通过管道传送到 sed 以产生相同的结果:

cat BSD | sed ''

您将看到文件的输出:

OutputCopyright (c) The Regents of the University of California.
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
    notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
    notice, this list of conditions and the following disclaimer in the
    documentation and/or other materials provided with the distribution.
. . .
. . .

如您所见,您可以对文件或文本流进行操作,就像使用管道 (|) 字符管道输出时产生的那些,同样容易。

印刷线

在前面的示例中,您看到传入 sed 的输入不经过任何操作就会将结果直接打印到标准输出。

让我们探索 sed 的显式 print 命令,您可以使用单引号内的 p 字符指定该命令。

执行以下命令:

sed 'p' BSD

您会看到 BSD 文件的每一行都打印了两次:

OutputCopyright (c) The Regents of the University of California.
Copyright (c) The Regents of the University of California.
All rights reserved.
All rights reserved.


Redistribution and use in source and binary forms, with or without
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
modification, are permitted provided that the following conditions
are met:
are met:
. . .
. . .

sed 默认情况下会自动打印每一行,然后你用“p”命令告诉它显式打印行,所以每行打印两次。

如果您仔细检查输出,您会发现它有两次第一行,然后是第二行两次,依此类推,这告诉您 sed 逐行对数据进行操作。 它读取一行,对其进行操作,并输出结果文本,然后在下一行重复该过程。

您可以通过将 -n 选项传递给 sed 来清理结果,这会抑制自动打印:

sed -n 'p' BSD
OutputCopyright (c) The Regents of the University of California.
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
    notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
    notice, this list of conditions and the following disclaimer in the
    documentation and/or other materials provided with the distribution.
. . .
. . .

我们现在回到打印每一行一次。

到目前为止的示例几乎不能被视为编辑(除非您想将每一行打印两次……)。 接下来,您将探索 sed 如何通过定位文本数据的特定部分来修改输出。

使用地址范围

地址可让您定位文本流的特定部分。 您可以指定特定的行,甚至可以指定行的范围。

sed 打印文件的第一行。 执行以下命令:

sed -n '1p' BSD

第一行打印到屏幕上:

OutputCopyright (c) The Regents of the University of California.

通过在打印命令之前放置数字 1,您可以告诉 sed 要操作的行号。 您可以轻松地打印五行(不要忘记“-n”):

sed -n '1,5p' BSD

你会看到这个输出:

OutputCopyright (c) The Regents of the University of California.
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions

您刚刚为 sed 提供了一个地址范围。 如果你给 sed 一个地址,它只会执行这些行后面的命令。 在此示例中,您已告诉 sed 打印第 1 行到第 5 行。 您可以通过给出第一个地址然后使用偏移量来告诉 sed 额外的行数来以不同的方式指定它,如下所示:

sed -n '1,+4p' BSD

这将产生相同的输出,因为您告诉 sed 从第 1 行开始,然后也对接下来的 4 行进行操作。

如果要每隔一行打印,请在 ~ 字符后指定间隔。 以下命令打印 BSD 文件中的每一行,从第 1 行开始:

sed -n '1~2p' BSD

这是您将看到的输出:

OutputCopyright (c) The Regents of the University of California.

modification, are permitted provided that the following conditions
1. Redistributions of source code must retain the above copyright
2. Redistributions in binary form must reproduce the above copyright
    documentation and/or other materials provided with the distribution.
    may be used to endorse or promote products derived from this software
. . .
. . .

您也可以使用 sed 从输出中删除文本。

删除文本

您可以通过将 p 命令更改为 d 命令来执行之前指定文本打印的文本删除。

在这种情况下,您不再需要 -n 命令,因为 sed 将打印所有未删除的内容。 这将帮助您了解正在发生的事情。

修改上一节中的最后一个命令,使其从第一行开始每隔一行删除:

sed '1~2d' BSD

结果是您会看到上次给出的 not 的每一行:

OutputAll rights reserved.
Redistribution and use in source and binary forms, with or without
are met:
    notice, this list of conditions and the following disclaimer.
    notice, this list of conditions and the following disclaimer in the
3. Neither the name of the University nor the names of its contributors
    without specific prior written permission.
. . .
. . .

需要注意的是,我们的源文件没有受到影响。 它仍然完好无损。 编辑输出到我们的屏幕。

如果我们想保存我们的编辑,我们可以将标准输出重定向到一个文件,如下所示:

sed '1~2d' BSD > everyother.txt

现在用 cat 打开文件:

cat everyother.txt

您会看到与之前在屏幕上看到的相同的输出:

OutputAll rights reserved.
Redistribution and use in source and binary forms, with or without
are met:
    notice, this list of conditions and the following disclaimer.
    notice, this list of conditions and the following disclaimer in the
3. Neither the name of the University nor the names of its contributors
    without specific prior written permission.
. . .
. . .

sed 命令默认不编辑源文件,但您可以通过传递 -i 选项来更改此行为,这意味着“就地执行编辑”。 这将改变源文件。

Warning:使用 -i 开关会覆盖原始文件,所以你应该小心使用它。 首先执行没有 -i 开关的操作,然后在获得所需内容后再次使用 -i 运行命令,创建原始文件的备份,或将输出重定向到文件。 使用 -i 开关很容易意外更改原始文件。


让我们通过就地编辑您刚刚创建的 everyother.txt 文件来尝试一下。 让我们通过再次删除每隔一行来进一步减少文件:

sed -i '1~2d' everyother.txt

如果您使用 cat 显示带有 cat everyother.txt 的文件,您会看到该文件已被编辑。

-i 选项可以是 危险的。 值得庆幸的是,sed 让您能够在编辑之前创建备份文件。

要在编辑之前创建备份文件,请在“-i”选项后直接添加备份扩展名:

sed -i.bak '1~2d' everyother.txt

这将创建一个扩展名为 .bak 的备份文件,然后就地编辑原始文件。

接下来您将了解如何使用 sed 执行搜索和替换操作。

替换文本

sed 最著名的用途可能是替换文本。 sed 可以使用正则表达式搜索文本模式,然后将找到的文本替换为其他内容。

您可以通过关注使用Grep正则表达式在Linux中搜索文本模式来了解更多关于正则表达式的知识。

在其最基本的形式中,您可以使用以下语法将一个单词更改为另一个单词:

's/old_word/new_word/'

s 是替代指令。 三个斜杠 (/) 用于分隔不同的文本字段。 如果更有帮助,您可以使用其他字符来分隔字段。

例如,如果您尝试更改网站名称,则使用另一个分隔符会很有帮助,因为 URL 包含斜杠。

执行以下命令打印带有 echo 的 URL 并使用 sed 修改它,使用下划线 (_) 字符作为分隔符:

echo "http://www.example.com/index.html" | sed 's_com/index_org/home_'

这将 com/index 替换为 org/home。 输出显示修改后的 URL:

Outputhttp://www.example.org/home.html

不要忘记最后的分隔符,否则 sed 会报错。 如果您运行此命令:

echo "http://www.example.com/index.html" | sed 's_com/index_org/home'

你会看到这个输出:

Outputsed: -e expression #1, char 20: unterminated `s' command

让我们创建一个新文件来练习一些替换。 执行以下命令创建一个名为 song.txt 的新文本文件:

echo "this is the song that never ends
yes, it goes on and on, my friend
some people started singing it
not knowing what it was
and they'll continue singing it forever
just because..." > song.txt

现在让我们将表达式 on 替换为 forward。 使用以下命令:

sed 's/on/forward/' song.txt

输出如下所示:

Outputthis is the sforwardg that never ends
yes, it goes forward and on, my friend
some people started singing it
not knowing what it was
and they'll cforwardtinue singing it forever
just because...

你可以在这里看到一些值得注意的事情。 首先,是 sed 替换了模式,而不是单词。 song 中的 on 更改为 forward

需要注意的另一件事是,在第 2 行,第二个 on 没有更改为 forward

这是因为默认情况下,s 命令对一行中的第一个匹配项进行操作,然后移动到下一行。 要使 sed 替换 on 的每个实例,而不仅仅是每行中的第一个,您必须将一个可选标志传递给替换命令。

通过将 g 标志放在替换集之后,将其提供给替换命令:

sed 's/on/forward/g' song.txt

你会看到这个输出:

Outputthis is the sforwardg that never ends
yes, it goes forward and forward, my friend
some people started singing it
not knowing what it was
and they'll cforwardtinue singing it forever
just because...

现在,替换命令会更改每个实例。

如果您 only 想要更改 sed 在每一行找到的第二个“on”实例,那么您将使用数字 2 而不是 g

sed 's/on/forward/2' song.txt

这次其他行没有改变,因为它们没有第二次出现:

Outputthis is the song that never ends
yes, it goes on and forward, my friend
some people started singing it
not knowing what it was
and they'll continue singing it forever
just because...

如果您只想查看替换了哪些行,请再次使用 -n 选项来禁止自动打印。

然后,您可以将 p 选项传递给替换命令以打印发生替换的行。

sed -n 's/on/forward/2p' song.txt

更改的行打印到屏幕上:

Outputyes, it goes on and forward, my friend

如您所见,您可以在命令末尾组合标志。

如果您希望搜索过程忽略大小写,可以将“i”标志传递给它。

sed 's/SINGING/saying/i' song.txt

这是您将看到的输出:

Outputthis is the song that never ends
yes, it goes on and on, my friend
some people started saying it
not knowing what it was
and they'll continue saying it forever
just because...

替换和引用匹配的文本

如果您想使用正则表达式查找更复杂的模式,您可以使用多种不同的方法来引用替换文本中的匹配模式。

例如,要从行首匹配 at,请使用以下命令:

sed 's/^.*at/REPLACED/' song.txt

你会看到这个输出:

OutputREPLACED never ends
yes, it goes on and on, my friend
some people started singing it
REPLACED it was
and they'll continue singing it forever
just because...

您可以看到通配符表达式从行首匹配到 at 的最后一个实例。

由于您不知道将在搜索字符串中匹配的确切短语,因此您可以使用 & 字符来表示替换字符串中的匹配文本。

让我们在匹配的文本周围加上括号:

sed 's/^.*at/(&)/' song.txt

你会看到这个输出:

Output(this is the song that) never ends
yes, it goes on and on, my friend
some people started singing it
(not knowing what) it was
and they'll continue singing it forever
just because...

引用匹配文本的一种更灵活的方法是使用转义括号对匹配文本的各个部分进行分组。

每组用括号标记的搜索文本都可以通过转义的参考号来引用。 例如,第一个括号组可以用 \1 引用,第二个括号组可以用 \2 引用,依此类推。

在此示例中,我们将切换每行的前两个单词:

sed 's/\([a-zA-Z0-9][a-zA-Z0-9]*\) \([a-zA-Z0-9][a-zA-Z0-9]*\)/\2 \1/' song.txt

你会看到这个输出:

Outputis this the song that never ends
yes, goes it on and on, my friend
people some started singing it
knowing not what it was
they and'll continue singing it forever
because just...

如您所见,结果并不完美。 例如,第二行跳过了第一个单词,因为它有一个未在我们的字符集中列出的字符。 同样,它在第五行将 they'll 视为两个单词。

让我们改进正则表达式使其更准确:

sed 's/\([^ ][^ ]*\) \([^ ][^ ]*\)/\2 \1/' song.txt

你会看到这个输出:

Outputis this the song that never ends
it yes, goes on and on, my friend
people some started singing it
knowing not what it was
they'll and continue singing it forever
because... just

这比上次好多了。 这会将标点符号与相关单词分组。

注意我们如何在括号内重复表达式(一次没有 * 字符,然后一次有它)。 这是因为 * 字符与它之前的字符集匹配零次或多次。 这意味着即使没有找到模式,与通配符的匹配也将被视为“匹配”。

为确保 sed 至少找到一次文本,您必须在不使用通配符的情况下匹配一次,然后才能使用通配符。

结论

在本教程中,您探索了 sed 命令。 您打印了文件中的特定行,搜索了文本,删除了行,覆盖了原始文件,并使用正则表达式替换了文本。 您应该已经能够看到如何使用正确构造的 sed 命令快速转换文本文档。

本系列的下一篇文章中,您将探索一些更高级的功能。