在第一次比赛之后使用sed插入行


244

由于某种原因,我似乎无法找到一个简单的答案,此刻我处于时间紧迫的状态。我将如何使用sed命令在与特定字符串匹配的第一行之后插入文本选择行。我有 ...

CLIENTSCRIPT="foo"
CLIENTFILE="bar"

我想在CLIENTSCRIPT=导致... 的行后插入一行

CLIENTSCRIPT="foo"
CLIENTSCRIPT2="hello"
CLIENTFILE="bar"

Answers:


378

尝试使用GNU sed执行此操作:

sed '/CLIENTSCRIPT="foo"/a CLIENTSCRIPT2="hello"' file

如果要替换就位,请使用

sed -i '/CLIENTSCRIPT="foo"/a CLIENTSCRIPT2="hello"' file

输出量

CLIENTSCRIPT="foo"
CLIENTSCRIPT2="hello"
CLIENTFILE="bar"

文件

  • 查看sed文档并进行搜索\a(附加)

谢谢!并使其写回文件而不打印文件内容?
user2150250

11
注意,两者都假定为GNU sed。这不是标准sed语法,因此无法与其他任何sed实现一起使用。
Stephane Chazelas 2014年

我很难使用此分隔符,因为我使用了不同的分隔符。RTFM'ing之后,我意识到我必须先以\,然后是定界符来启动正则表达式。
克里斯蒂

10
它如何仅适用于首场比赛?很明显,它在比赛之后附加了文字,但是如何只知道第一个比赛呢?
Mohammed Noureldin

7
对于我来说,这会在每次比赛后添加文字。
schoppenhauer

49

请注意标准sed语法(与POSIX中一样,因此受所有符合的sed实现(GNU,OS / X,BSD,Solaris ...)支持):

sed '/CLIENTSCRIPT=/a\
CLIENTSCRIPT2="hello"' file

或一行:

sed -e '/CLIENTSCRIPT=/a\' -e 'CLIENTSCRIPT2="hello"' file

-expression(和ile的内容-f)与换行符一起组成sed脚本sed解释)。

-i就地编辑的选项也是GNU扩展,其他一些实现(例如FreeBSD的)也-i ''对此提供了支持。

另外,为了可移植性,您可以perl改用:

perl -pi -e '$_ .= qq(CLIENTSCRIPT2="hello"\n) if /CLIENTSCRIPT=/' file

或者您可以使用edex

printf '%s\n' /CLIENTSCRIPT=/a 'CLIENTSCRIPT2="hello"' . w q | ex -s file

2
我可能是错的,但是电流sed -e '/CLIENTSCRIPT=/a\' -e 'CLIENTSCRIPT2="hello"' file在第一个参数末尾转义了引号并中断了命令。
被遗弃的购物车

1
在Bourne(cshrc家族)的外壳中,@ AbandonedCart '...'是强引号,里面的反斜杠并不特殊。我知道的唯一例外是fish外壳。
Stephane Chazelas

1
显然,MacOS上的终端(以及随后在终端中运行的脚本)也是一个例外。我发现了其他语法,但是还是谢谢。
被遗弃的购物车

1
@AbandonedCart,那是另外一回事。这是sed不兼容POSIX 的macOS 。这使我对其可移植性的陈述不正确(对于单行变体)。我会请opengroup确认是否确实不符合标准或对我的标准有误解。
Stephane Chazelas

18

POSIX兼容的,使用以下s命令:

sed '/CLIENTSCRIPT="foo"/s/.*/&\
CLIENTSCRIPT2="hello"/' file

1
...甚至支持插入新行。我喜欢它!
Stephan Henningsen

1
另请参见sed -e '/.../s/$/\' -e 'CLI.../'这将避免包含字节序列的行不构成有效字符的问题。
斯蒂芬·夏泽拉斯

14

可以在MacOS(至少是OS 10)和Unix上运行的sed命令(即,不需要像Gilles(目前接受)那样使用gnu sed):

sed -e '/CLIENTSCRIPT="foo"/a\'$'\n''CLIENTSCRIPT2="hello"' file

这可以在bash和可能知道$'\ n'评估引用样式的其他shell中使用。一切都可以在同一行上,并且可以在旧版/ POSIX sed命令中运行。如果可能有多行与CLIENTSCRIPT =“ foo”(或您的等效行)匹配,并且您希望仅在第一次添加额外的行,则可以按以下方式重新处理:

sed -e '/^ *CLIENTSCRIPT="foo"/b ins' -e b -e ':ins' -e 'a\'$'\n''CLIENTSCRIPT2="hello"' -e ': done' -e 'n;b done' file

(这将在行插入代码之后创建一个循环,该循环仅循环遍历文件的其余部分,而永远不会再回到第一个sed命令)。

您可能会注意到我在匹配的模式中添加了'^ *',以防该行显示在注释中,例如说是缩进的。它不是100%完美,但涵盖了其他可能很常见的情况。根据需要进行调整...

这两个解决方案还解决了问题(对于添加行的通用解决方案),如果新插入的行包含未转义的反斜杠或“&”号,它们将被sed解释,并且可能不一样,例如\n是-。\0将是匹配的第一行。如果您要添加一行来自变量的行,则特别方便,否则必须先使用$ {var //}或其他sed语句等先对所有内容进行转义。

如果您不想将a命令的替换文本放在一行的开头(例如,在函数中),则此解决方案在脚本中的混乱程度会降低(尽管引号和\ n并不容易阅读)缩进线。我利用了$'\ n'由shell评估为换行符的优点,而不是用常规的'\ n'单引号引起来。

尽管它足够长,但我认为perl / even awk可能会因更具可读性而获胜。


1
谢谢您的回答!第一个在AIX OS上也像魅力一样起作用。
abhishek

除了多行插入外,您该如何做?
tatsu

1
如果您用反斜杠“转义”(),则POSIX sed支持替换字符串中的文字换行符。因此:s/CLIENTSCRIPT="foo"/&\ [Enter] CLIENTSCRIPT2="hello"/。反斜杠必须是该行的最后一个字符(其后没有空格),这与该注释中的样子相反。
TheDudeAbides

1
可以使用我在上面的注释中描述的相同方法来“转义” 替换字符串中的@tatsu换行符s///以及ai命令中的@tatsu换行符。
TheDudeAbides

3

对此发布答案可能有点晚,但是我发现上述解决方案有些繁琐。

我在sed中尝试了简单的字符串替换,它的工作原理是:

sed 's/CLIENTSCRIPT="foo"/&\nCLIENTSCRIPT2="hello"/' file

符号反映匹配的字符串,然后添加\ n和新行。

如前所述,如果您想就地进行:

sed -i 's/CLIENTSCRIPT="foo"/&\nCLIENTSCRIPT2="hello"/' file

另一件事。您可以使用表达式进行匹配:

sed -i 's/CLIENTSCRIPT=.*/&\nCLIENTSCRIPT2="hello"/' file

希望这可以帮助某人


这在Linux(默认为GNU sed)上可以正常工作,因为它\n在替换字符串中可以理解。纯香草(POSIX)sed明白\n替换字符串中的,然而,这不会对BSD或者你已经安装了GNU莫名其妙的sed除非MacOS的工作。使用香草sed,您可以通过“转义”换行符嵌入换行符,即以反斜杠结束该行,然后按[Enter](在标题下的参考[2addr]s/BRE/replacement/flags)。这意味着您的sed命令必须在终端中跨越多行。
TheDudeAbides

在替换字符串中获取文字换行符的另一种方法是使用Bash中的ANSI-C引用功能,如其他答案之一所述
TheDudeAbides


1

我有类似的任务,但无法使上述perl解决方案正常工作。

这是我的解决方案:

perl -i -pe "BEGIN{undef $/;} s/^\[mysqld\]$/[mysqld]\n\ncollation-server = utf8_unicode_ci\n/sgm" /etc/mysql/my.cnf

说明:

使用正则表达式在我的/etc/mysql/my.cnf文件中搜索仅包含[mysqld]并替换为的行

[mysqld] collation-server = utf8_unicode_ci

有效地collation-server = utf8_unicode_ci在包含的行之后添加行[mysqld]


0

我最近在Mac和Linux OS上也都必须这样做,在浏览了许多文章并尝试了许多方法之后,我特别认为我从来没有想过要去的地方:使用已知的方法足以理解解决方案以及带有简单模式的标准命令,一根衬垫,可移植,可扩展以增加更多约束。然后,我尝试以不同的角度来看待它,那时候我意识到,如果“ 2-衬里”满足我的其他标准,我可以不用“一个衬里”选项。最后,我想出了这个解决方案,我希望它可以在Ubuntu和Mac上使用,我想与大家分享:

insertLine=$(( $(grep -n "foo" sample.txt | cut -f1 -d: | head -1) + 1 ))
sed -i -e "$insertLine"' i\'$'\n''bar'$'\n' sample.txt

在第一个命令中,grep查找包含“ foo”的行号,cut / head选择第一个出现的行,并且由于我想在出现后插入,算术op将第一个出现的行号加1。在第二个命令中,它是就地文件编辑,用于插入“ i”:用ansi-c引用新行,“ bar”,然后是另一行。结果是在“ foo”行之后添加了一个包含“ bar”的新行。这两个命令中的每一个都可以扩展为更复杂的操作和匹配。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.