使用 sed 在匹配后插入行

Posted

技术标签:

【中文标题】使用 sed 在匹配后插入行【英文标题】:Insert line after match using sed 【发布时间】:2013-03-11 15:52:19 【问题描述】:

由于某种原因,我似乎无法找到一个直接的答案,而且我现在有点时间紧张。我将如何使用sed 命令在匹配特定字符串的第一行之后插入选择的文本行。我有...

CLIENTSCRIPT="foo"
CLIENTFILE="bar"

我想在CLIENTSCRIPT= 行之后插入一行,结果...

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

【问题讨论】:

如果需要在插入的行中使用捕获组,请检查此问题***.com/q/39103787/520567 【参考方案1】:

我最近也必须为 Mac 和 Linux 操作系统执行此操作,并且在浏览了许多帖子并尝试了许多事情之后,在我看来,我从来没有到达我想要的地方:一个足够简单易懂的解决方案使用具有简单模式的众所周知的标准命令,一个班轮,便携,可扩展以添加更多约束。然后我尝试从不同的角度看待它,那时我意识到如果“2-liner”满足我的其他标准,我可以不使用“one liner”选项。最后我想出了这个我喜欢的解决方案,它适用于 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”的新行。这两个命令中的每一个都可以扩展为更复杂的操作和匹配。

【讨论】:

【参考方案2】:

awk 变体:

awk '1;/CLIENTSCRIPT=/print "CLIENTSCRIPT2=\"hello\""' file

【讨论】:

任何想知道1;的人,请参阅Why does “1” in awk print the current line?【参考方案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。 Plain-vanilla (POSIX) sed 确实 not 理解替换字符串中的 \n,但是,这在 BSD 或 macOS 上不起作用——除非你以某种方式安装了 GNU sed。使用 vanilla sed,您可以通过“转义”它们来嵌入换行符——也就是说,用反斜杠结束行,然后按 [Enter](reference,在标题下对于[2addr]s/BRE/replacement/flags)。这意味着您的 sed 命令必须跨越终端中的多行。 在替换字符串中获取文字换行符的另一个选项是使用 Bash 中的 ANSI-C quoting 功能,如 one of the other answers 中所述。【参考方案4】:

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

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

如果你想就地替换,请使用

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

输出

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

文档

查看sed doc 并搜索\a(附加)

【讨论】:

谢谢!并让它写回文件而不打印文件的内容? 请注意,两者都假定 GNU sed。这不是标准的 sed 语法,并且不适用于任何其他 sed 实现。 我无法让它为我工作,因为我使用了不同的分隔符。在 RTFM'ing 之后,我意识到我必须用 \ 开始正则表达式,然后是分隔符。 它如何仅适用于第一场比赛?很明显它会在匹配后附加文本,但它如何只知道第一个匹配? 对我来说,这会在每场比赛后添加文本。【参考方案5】:

使用s 命令的POSIX 兼容:

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

【讨论】:

...甚至支持插入新行。我喜欢它! 另见sed -e '/.../s/$/\' -e 'CLI.../',这将避免包含不形成有效字符的字节序列的行出现问题。【参考方案6】:

注意标准的sed 语法(如在 POSIX 中,因此被所有符合标准的sed 实现支持(GNU、OS/X、BSD、Solaris...)):

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

或单行:

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

-expressions(和-files 的内容)与换行符连接起来组成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

【讨论】:

我可能错了,但是当前的sed -e '/CLIENTSCRIPT=/a\' -e 'CLIENTSCRIPT2="hello"' file 转义了第一个参数末尾的引号并中断了命令。 @AbandonedCart,在 Bourne 的 shell 中,cshrc 家族,'...' 是强引号,其中反斜杠并不特殊。我知道的唯一例外是fish shell。 MacOS 上的终端(以及随后在终端中运行的脚本)显然也是一个例外。我找到了替代语法,但还是谢谢。 @AbandonedCart,那是另一回事。那是 macOS sed 在这里不符合 POSIX。这使得我关于它是可移植的声明不正确(对于单行变体)。如果我确实不符合标准或对标准的误解,我会要求 opengroup 确认。 我在使用不同版本的 sed 时遇到了很多问题,因此我尝试了 printf...ex 版本。这就像魅力!【参考方案7】:

适用于 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 解释并且可能不一样,就像 @ 987654324@ 是 - 例如。 \0 将是匹配的第一行。如果您要添加来自变量的行,否则您必须先使用 $var// 或另一个 sed 语句等来转义所有内容,则特别方便。

当您不想将 a 命令的替换文本放在行首时,如果您不想将 a 命令的替换文本放在行首,那么此解决方案在脚本中的混乱程度会降低一些(尽管引用和 \n 不易阅读),在带有缩进线的函数中。我利用 $'\n' 由 shell 评估为换行符,而不是常规的 '\n' 单引号值。

虽然我认为 perl/even awk 可能会因为更具可读性而获胜,但它已经足够长了。

【讨论】:

感谢您的回答!第一个同样适用于 AIX OS。 除了多行插入,你怎么做? POSIX sed 支持替换字符串中的文字换行符,如果您使用反斜杠 (source) “转义”它们。所以:s/CLIENTSCRIPT="foo"/&\ [Enter] CLIENTSCRIPT2="hello"/。反斜杠必须是该行的最后一个字符(后面没有空格),与此注释中的外观相反。 @tatsu s/// 的替换字符串中的换行符,以及ai 命令中的换行符可以使用我在上面评论中描述的相同方法“转义” .【参考方案8】:

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

这是我的解决方案:

perl -i -pe "BEGINundef $/; 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

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

【讨论】:

以上是关于使用 sed 在匹配后插入行的主要内容,如果未能解决你的问题,请参考以下文章

sed 在指定行插入?

sed在匹配行前面或者后面插入一行

sed在匹配行前面或者后面插入一行

sed 在匹配行前后添加内容

sed在匹配行之前插入文件内容

需要帮助在 macOS 上使用 sed 在两次连续匹配后插入文本 [关闭]