如何在Linux上使用sed命令

时间:2020-01-09 10:39:56  来源:igfitidea点击:

sed的力量

sed是一种流编辑器,适用于管道输入或者文本文件。但是,它没有交互式文本编辑器界面。相反,我们将提供指示信息以使其在文本中有效。所有这些都可以在Bash和其他命令行Shell中使用。

使用sed,我们可以执行以下所有操作:

  • 选择文字

  • 替代文字

  • 在文本中添加行

  • 删除文字行

  • 修改(或者保留)原始文件

但是,sed的模式匹配和文本选择功能在很大程度上依赖于正则表达式(regexes)。我们将需要对它们有一定的了解,以充分利用sed。

一个简单的例子

首先,我们将使用" echo"将一些文本通过管道发送到" sed",并用" sed"代替部分文本。为此,我们键入以下内容:

echo howtogonk | sed 's/gonk/data/'

echo命令将howtogonk发送到sed中,并应用我们的简单替换规则(s表示替换)。sed在输入文本中搜索第一个字符串的出现,并将所有匹配项替换为第二个字符串。

字符串gonk替换为data,新的字符串打印在终端窗口中。

替代可能是sed的最常见用法。但是,在我们深入研究替代之前,我们需要知道如何选择和匹配文本。

选择文字

我们的示例将需要一个文本文件。我们将使用其中包含选自塞缪尔·泰勒·科尔里奇(Samuel Taylor Coleridge)的诗篇《远古水手的降世》中的诗句。

我们输入以下内容以使用less进行查看:

less coleridge.txt

要从文件中选择一些行,我们提供要选择的范围的开始和结束行。单个数字选择该行。

要提取第一到第四行,我们输入以下命令:

sed -n '1,4p' coleridge.txt

注意" 1"和" 4"之间的逗号。 p表示打印匹配的行。默认情况下,sed打印所有行。我们会在文件中看到所有文本,并且匹配行打印两次。为了防止这种情况,我们将使用-n(安静)选项来抑制不匹配的文本。

我们更改行号,以便可以选择其他经文,如下所示:

sed -n '6,9p' coleridge.txt

我们可以使用-e(表达式)选项进行多个选择。通过两个表达式,我们可以选择两个经文,如下所示:

sed -n -e '1,4p' -e '31,34p' coleridge.txt

如果我们减少第二个表达式中的第一个数字,我们可以在两个经文之间插入一个空格。我们输入以下内容:

sed -n -e '1,4p' -e '30,34p' coleridge.txt

我们还可以选择一个起始行,并告诉sed'逐步遍历文件并每隔第五行打印备用行,或者跳过任何行。该命令与我们上面用来选择范围的命令相似。但是,这一次,我们将使用代字号(~`)而不是逗号来分隔数字。

第一个数字表示起点。第二个数字告诉sed我们要在起始行之后的哪几行。数字2表示每隔第二行,数字3表示每隔三行,依此类推。

我们输入以下内容:

sed -n '1~2p' coleridge.txt

我们不会总是知道要查找的文本在文件中的位置,这意味着行号不会总是很有帮助。但是,我们也可以使用sed选择包含匹配文本模式的行。例如,让我们提取以And开头的所有行。

尖号(^)代表行的开始。我们将搜索词括在正斜杠(/)中。我们在And后面加上空格,因此结果中不会包含Android之类的字词。

起初阅读sed脚本可能有点困难。 / p表示打印,就像上面我们使用的命令一样。但是,在以下命令中,其前面是一个正斜杠:

sed -n '/^And /p' coleridge.txt

从文件中提取以And开头的三行,并为我们显示。

替换

在第一个示例中,我们向我们展示了sed替换的以下基本格式:

echo howtogonk | sed 's/gonk/data/'

s告诉sed这是一个替代。第一个字符串是搜索模式,第二个字符串是我们要替换匹配文本的文本。当然,与所有Linux一样,细节在于魔鬼。

我们键入以下内容来更改每天的所有出现,并给水手和信天翁更多的时间进行绑定:

sed -n 's/day/week/p' coleridge.txt

在第一行中,仅更改了第二天。这是因为sed在每行的第一个匹配之后停止。我们必须在表达式的末尾添加g,如下所示,以执行全局搜索,以便处理每行中的所有匹配项:

sed -n 's/day/week/gp' coleridge.txt

这与第一行中的四个匹配。因为第一个单词是Day,并且sed区分大小写,所以它认为该实例与day相同。

我们键入以下内容,在表达式末尾的命令中添加i表示不区分大小写:

sed -n 's/day/week/gip' coleridge.txt

这可行,但是我们可能并不总是希望对所有内容都启用不区分大小写的功能。在这些情况下,我们可以使用正则表达式组来添加特定于模式的不区分大小写。

例如,如果我们将字符括在方括号([])中,它们将被解释为该字符列表中的任何字符。

我们输入以下内容,并在组中包括D和d,以确保它与Day和day都匹配:

sed -n 's/[Dd]ay/week/gp' coleridge.txt

我们还可以将替换限制在文件的各个部分。假设我们的文件在第一节经文中包含奇怪的空格。我们可以使用以下熟悉的命令来查看第一节经文:

sed -n '1,4p' coleridge.txt

我们将搜索两个空格,并用一个空格代替。我们将在全局范围内执行此操作,以便在整个生产线上重复执行此操作。明确地说,搜索模式是空格,星号(`*)和替换字符串是单个空格。 '1,4'将替换限制为文件的前四行。

我们将所有这些放到以下命令中:

sed -n '1,4 s/  */ /gp' coleridge.txt

这很好用!搜索模式在这里很重要。星号(*)表示零个或者多个前一个字符,即空格。因此,搜索模式正在寻找一个或者多个空格的字符串。

如果将单个空格替换为多个空格的任何序列,则将文件恢复为常规间距,每个单词之间使用单个空格。在某些情况下,这也会用一个空格代替一个空格,但这不会对任何事情产生不利影响,我们仍然会得到我们想要的结果。

如果输入以下内容并将搜索模式减少到一个空格,我们将立即明白为什么必须包含两个空格:

sed -n '1,4 s/ */ /gp' coleridge.txt

由于星号与零个或者多个前一个字符匹配,因此它将不是空格的每个字符都视为零空格,并对其进行替换。

但是,如果我们在搜索模式中包含两个空格,则sed必须在应用替换之前找到至少一个空格字符。这样可以确保非空格字符保持不变。

我们使用之前使用的-e(表达式)键入以下内容,这使我们可以同时进行两个或者多个替换:

sed -n -e 's/motion/flutter/gip' -e 's/ocean/gutter/gip' coleridge.txt

如果使用分号(;)分隔两个表达式,则可以实现相同的结果,如下所示:

sed -n 's/motion/flutter/gip;s/ocean/gutter/gip' coleridge.txt

当我们在以下命令中将天换成星期时,表达式a-day中的day实例也被交换了:

sed -n 's/[Dd]ay/week/gp' coleridge.txt

为了防止这种情况,我们只能在与另一个模式匹配的行上尝试替换。如果我们修改命令以使其在开始时具有搜索模式,则仅考虑在与该模式匹配的行上进行操作。

我们输入以下内容,使匹配的模式成为以下单词:

sed -n '/after/ s/[Dd]ay/week/gp' coleridge.txt

这给了我们我们想要的回应。

更复杂的替代

让我们给Coleridge一个休息,并使用sedetc / passwd文件中提取名字。

有较短的方法可以做到这一点(稍后会详细介绍),但是我们将在此处使用较长的方法来演示另一个概念。搜索模式中的每个匹配项目(称为子表达式)都可以编号(最多9个项目)。然后,我们可以在sed命令中使用这些数字来引用特定的子表达式。

我们必须将子表达式括在括号[()]中才能正常工作。括号前还必须带有反斜杠(),以防止将其视为普通字符。

为此,我们将键入以下内容:

sed 's/\([^:]*\).*//' /etc/passwd

让我们分解一下:

  • sed's /sed命令和替换表达式的开头。

  • \(:括在子表达式中的圆括号[(),后跟反斜杠()。

  • [^:] *:搜索词的第一个子表达式在方括号中包含一组。尖号(^)表示不在组中使用时。组表示任何非冒号()的字符都将被接受为匹配项。

  • \):右括号()]前加反斜杠()。

  • 。*:第二个搜索子表达式表示任意字符和任意数量的字符。

  • " / \ 1":表达式的替换部分包含" 1",后跟反斜杠(" ")。这表示与第一个子表达式匹配的文本。

  • /':右斜杠(/)和单引号(')终止sed命令。

这一切都意味着我们将查找不包含冒号()的任何字符串,这将是匹配文本的第一个实例。然后,我们正在搜索该行上的任何其他内容,这将是匹配文本的第二个实例。我们将用与第一个子表达式匹配的文本替换整行。

/ etc / passwd文件中的每一行都以冒号结尾的用户名开头。我们将所有内容匹配到第一个冒号,然后将该值替换为整行。因此,我们隔离了用户名。

接下来,将第二个子表达式括在括号[()]中,以便我们也可以按数字引用它。我们还将用\ 2替换\ 1. 现在,我们的命令将用第一个冒号()到行尾的所有内容替换整个行。

我们输入以下内容:

sed 's/\([^:]*\)\(.*\)//' /etc/passwd

这些小的更改使命令的含义反转,除用户名外,我们获得了所有内容。

现在,让我们看一下执行此操作的快速简便的方法。

我们的搜索词是从第一个冒号()到行尾。由于我们的替换表达式为空(//),因此我们不会将匹配的文本替换为任何内容。

因此,我们键入以下内容,从第一个冒号()到行尾的所有内容都切掉,仅保留用户名:

sed 's/:.*//" /etc/passwd

让我们看一个示例,其中我们在同一命令中引用了第一和第二个匹配项。

我们有一个用逗号()分隔名字和姓氏的文件。我们想将它们列出为姓氏,名字。如下所示,我们可以使用cat来查看文件中的内容:

cat datas.txt

就像许多sed命令一样,下一个命令乍一看似乎不可理解:

sed 's/^\(.*\),\(.*\)$/, /g' datas.txt

与我们使用的其他命令一样,这是一个替换命令,搜索模式非常简单。我们将其分解如下:

  • sed's /`:常规替换命令。

  • ^:因为插入符号不在组中([]),所以它表示行的开始。

  • \(。* \),:第一个子表达式是任意数量的任何字符。它括在括号[()]中,每个括号前面都有一个反斜杠(),因此我们可以按数字引用它。到目前为止,我们的整个搜索模式都是从行首到第一个逗号()进行搜索,以查找任意数量的任何字符。

  • \(。* \):下一个子表达式(再次)是任意数量的任意字符。它也括在括号[()]中,两个括号均以反斜杠()开头,因此我们可以按数字引用匹配的文本。

  • $ /:美元符号($)代表该行的结尾,并将使我们的搜索继续到该行的结尾。我们只是用它来引入美元符号。我们在这里实际上并不需要它,因为在这种情况下,星号(*)会到达行尾。正斜杠(/`)构成搜索模式部分。

  • \ 2,\ 1 / g':因为我们将两个子表达式的括号括起来,所以我们可以通过它们的编号来引用它们。因为我们想颠倒顺序,所以我们将它们键入为" second-match,first-match"。数字之前必须加反斜杠(\)。

  • / g:这使我们的命令可以在每行全局运行。

  • datas.txt:我们正在处理的文件。

我们也可以使用Cut命令(c)替换与搜索模式匹配的整行。我们键入以下内容以搜索其中包含单词neck的行,然后将其替换为新的文本字符串:

sed '/neck/c Around my wrist was strung' coleridge.txt

现在,我们的新行显示在摘要的底部。

插入行和文本

我们还可以在文件中插入新行和文本。要在任何匹配的行之后插入新行,我们将使用Append命令(a)。

这是我们要使用的文件:

cat datas.txt

我们对行进行了编号,以使其更容易理解。

我们键入以下内容以搜索包含单词He的行,并在其下插入新行:

sed '/He/a --> Inserted!' datas.txt

我们输入以下内容,并包含插入命令(i),以便在包含匹配文本的行上方插入新行:

sed '/He/i --> Inserted!' datas.txt

我们可以使用代表原始匹配文本的&字符(&)将新文本添加到匹配行。 \ 1,\ 2等代表匹配的子表达式。

要将文本添加到行的开头,我们将使用匹配该行中所有内容的替换命令,并使用将新文本与原始行组合在一起的替换子句。

为此,我们键入以下内容:

sed 's/.*/--> Inserted &/' datas.txt

我们输入以下内容,包括" G"命令,该命令将在每行之间添加空白行:

sed 'G' datas.txt

如果要添加两个或者多个空行,可以使用G; GG; G; G等。

删除行

Delete命令(d)删除与搜索模式匹配的行,或者由行号或者范围指定的行。

例如,要删除第三行,我们将键入以下内容:

sed '3d' datas.txt

要删除第4至5行的范围,请输入以下内容:

sed '4,5d' datas.txt

要删除范围之外的行,我们使用感叹号(),如下所示:

sed '6,7!d' datas.txt

保存更改

到目前为止,我们所有的结果都已打印到终端窗口,但是我们还没有将它们保存在任何地方。要永久保留这些内容,我们可以将更改写入原始文件,也可以将其重定向到新文件。

覆盖原始文件需要谨慎。如果" sed"命令是错误的,我们可能会对原始文件进行一些难以撤消的更改。

为了让我们高枕无忧,sed可以在执行命令之前创建原始文件的备份。

我们可以使用就地选项(-i)来告诉sed将更改写入原始文件,但是如果我们向其添加文件扩展名,则sed会将原始文件备份到新文件一。它将具有与原始文件相同的名称,但具有新的文件扩展名。

为了演示,我们将搜索包含单词He的所有行并将其删除。我们还将使用BAK扩展名将原始文件备份到新文件。

为此,我们键入以下内容:

sed -i'.bak' '/^.*He.*$/d' datas.txt

我们输入以下内容以确保我们的备份文件未更改:

cat datas.txt.bak

我们还可以键入以下命令将输出重定向到新文件并获得类似的结果:

sed -i'.bak' '/^.*He.*$/d' datas.txt > new_datas.txt

我们使用cat确认更改已写入新文件,如下所示:

cat new_datas.txt