bash 用 sed 搜索和替换多行模式
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/37902801/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Search and replace a multi-line pattern with sed
提问by Mozzy
I've seen a lot of examples regarding this problem, but have yet to see the one that is tailored for my use. Although I'm very familiar with sed I'm ashamed to say that I'm a noob when it comes to more advanced features. Here's the problem at hand.
我已经看过很多关于这个问题的例子,但还没有看到适合我使用的例子。尽管我对 sed 非常熟悉,但在谈到更高级的功能时,我很惭愧地说我是个菜鸟。这是手头的问题。
I have a multi-line pattern that I can successfully match with sed like so,
我有一个多行模式,我可以像这样成功地与 sed 匹配,
sed -e '/Favorite Animals/, /!/p
sed -e '/Favorite Animals/, /!/p
Favorite Animals
Monkey
Penguin
Cat
!
Favorite Things
Shoe
Dog
Wheel
Moth
!
and what I personally like about this expression is that I can match a variable number of lines up to the exclamation character. Now let's say I wanted to do a search and replace on that same pattern. Basically I would like to replace that multi-line pattern that was previously demonstrated with any string of my choosing. Any Ideas? I'm hoping for similar syntax to my demonstrated sed command, but beggars can't be choosers.
我个人喜欢这个表达式的地方在于,我可以将可变数量的行匹配到感叹号。现在假设我想在相同的模式上进行搜索和替换。基本上我想用我选择的任何字符串替换之前演示的多行模式。有任何想法吗?我希望有与我演示的 sed 命令类似的语法,但乞丐不能挑剔。
The idea is so that I can replace one of the groups delimited by the exclamation with a string. I'll call them "entries". I want to be able to update or overwrite these entries. If I had a new updated version of favorite animals I would like to be able to replace the old entry with a new one like this.
这个想法是我可以用字符串替换由感叹号分隔的组之一。我将它们称为“条目”。我希望能够更新或覆盖这些条目。如果我有最喜欢的动物的新更新版本,我希望能够用这样的新条目替换旧条目。
Favorite Animals
Sloth
Platypus
Badger
Dog
!
Favorite Things
Shoe
Dog
Wheel
Moth
!
As you can see I'm no longer a fan of monkeys now.
正如你所看到的,我现在不再是猴子的粉丝了。
采纳答案by Jonathan Leffler
There are a variety of options — the i
, c
, and a
commands can all be used.
有多种选择-的i
,c
和a
都可以使用的命令。
Amended answer
修正答案
This amended answer deals with the modified data file now in the question. Here's a mildly augmented version of the modified data
file:
这个修改后的答案现在处理问题中修改后的数据文件。这是修改后的data
文件的轻度增强版本:
There's material at the start of the file then the key information:
Favourite Animals
Monkey
Penguin
Cat
!
Favourite Things
Shoe
Wheel
Moth
!
and some material at the end of the file too.
All three of these sed
scripts produce the same output:
所有这三个sed
脚本都产生相同的输出:
sed '/Favourite Animals/,/!/c\
Favourite Animals\
Sloth\
Platypus\
Badger\
!
' data
sed '/Favourite Animals/i\
Favourite Animals\
Sloth\
Platypus\
Badger\
!
/Favourite Animals/,/!/d' data
sed '/Favourite Animals/a\
Favourite Animals\
Sloth\
Platypus\
Badger\
!
/Favourite Animals/,/!/d' data
Sample output:
示例输出:
There's material at the start of the file then the key information:
Favourite Animals
Sloth
Platypus
Badger
!
Favourite Things
Shoe
Wheel
Moth
!
and some material at the end of the file too.
It is crucial that the scripts all use the unique string, /Favourite Animals/
and do not key off the repeated trailing context /!/
. If the i
or a
use /!/
instead of /Favourite Animals/
, the outputs change — and not for the better.
脚本都使用唯一的字符串,/Favourite Animals/
并且不要关闭重复的尾随上下文,这一点至关重要/!/
。如果i
还是a
使用/!/
替代/Favourite Animals/
,输出的变化-而不是更好。
/!/i
:
/!/i
:
There's material at the start of the file then the key information:
Favourite Animals
Sloth
Platypus
Badger
!
Favorite Things
Shoe
Wheel
Moth
Favourite Animals
Sloth
Platypus
Badger
!
!
and some material at the end of the file too.
/!/a
:
/!/a
:
There's material at the start of the file then the key information:
Favourite Animals
Sloth
Platypus
Badger
!
Favorite Things
Shoe
Wheel
Moth
!
Favourite Animals
Sloth
Platypus
Badger
!
and some material at the end of the file too.
Extra request
额外要求
Would it be possible to select a range within a range using sed? Basically, what if I wanted to change or remove one/many of my favorite animals within the previously specified range. That is
/Favorite Animals/,/!/...
change something within this range.
是否可以使用 sed 在一个范围内选择一个范围?基本上,如果我想在之前指定的范围内更改或删除我最喜欢的一种/多种动物怎么办。那就是
/Favorite Animals/,/!/...
在这个范围内改变一些东西。
Yes, of course. For a single mapping:
是的当然。对于单个映射:
sed '/Favourite Animals/,/!/ s/Monkey/Gorilla/'
For multiple mappings:
对于多个映射:
sed '/Favourite Animals/,/!/ {
s/Monkey/Gorilla/
s/Penguin/Zebra/
s/Cat/Dog/
}'
You can also combine those onto a single line if you wish — use semicolons to separate them:
如果你愿意,你也可以将它们组合成一行——使用分号来分隔它们:
sed '/Favourite Animals/,/!/ { s/Monkey/Gorilla/; s/Penguin/Zebra/; s/Cat/Dog/; }'
Be aware that GNU sed
and BSD (Mac OS X) sed
have different views on the necessity for the last semicolon — what's shown works with both.
请注意,GNUsed
和 BSD (Mac OS X)sed
对最后一个分号的必要性有不同的看法——所显示的内容对两者都适用。
The original answer works with a simpler input file.
原始答案适用于更简单的输入文件。
Original answer
原答案
Consider the file data
containing:
考虑data
包含以下内容的文件:
There's material
at the start of the file
then the key information:
Favourite Animals
Monkey
Penguin
Cat
!
and material at the end of the file too.
Using c
, you might write:
使用c
,你可能会写:
$ sed '/Favourite Animals/,/!/c\
> Replacement material\
> for the favourite animals
> ' data
There's material
at the start of the file
then the key information:
Replacement material
for the favourite animals
and material at the end of the file too.
$
Using i
, you would use:
使用i
,您将使用:
$ sed '/Favourite Animals/i\
> Replacement material\
> for the favourite animals
> /Favourite Animals/,/!/d' data
There's material
at the start of the file
then the key information:
Replacement material
for the favourite animals
and material at the end of the file too.
$
Using a
, you might write:
使用a
,你可能会写:
$ sed '/!/a\
> Replacement material\
> for the favourite animals
> /Favourite Animals/,/!/d' data
There's material
at the start of the file
then the key information:
Replacement material
for the favourite animals
and material at the end of the file too.
$
Note that with:
请注意,与:
c
—?you change the whole rangei
— you insert before the first pattern in the range before you delete the entire rangea
— you append after the last pattern in the range before you delete the entire range
c
—?你改变了整个范围i
— 在删除整个范围之前插入范围中的第一个模式a
- 在删除整个范围之前,在范围中的最后一个模式之后追加
Though, come to think of it, you could insert before the last pattern in the range before deleting the entire range, or append after the first pattern in the range before deleting the entire range. So, the key with i
and a
is to put the 'replacement text' operation before the range-based delete. But c
is most succinct.
不过,想想看,您可以在删除整个范围之前在范围中的最后一个模式之前插入,或者在删除整个范围之前在范围中的第一个模式之后追加。因此,用钥匙i
和a
之前的范围内,基于删除把“替换文本”操作。但c
最简洁。
回答by Beta
The i
(insert) command has ugly syntax, but it works:
在i
(插入)命令有丑陋的语法,但它的工作原理:
sed '/Favorite Animals/i\
some new text\
some more new text\
and a little more
/Favorite Animals/,/!/d'