如何在Linux上使用sed命令
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一个休息,并使用sed
从etc / 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; G
,G; 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