ChatGPT解决这个技术问题 Extra ChatGPT

使用 sed 在匹配后插入行

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

CLIENTSCRIPT="foo"
CLIENTFILE="bar"

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

CLIENTSCRIPT="foo"
CLIENTSCRIPT2="hello"
CLIENTFILE="bar"
如果需要在插入的行中使用捕获组,请检查此问题 stackoverflow.com/q/39103787/520567

D
Dessa Simpson

尝试使用 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(附加)


请注意,两者都假设 GNU sed。这不是标准的 sed 语法,并且不适用于任何其他 sed 实现。
我很难让它为我工作,因为我使用了不同的分隔符。在 RTFM'ing 之后,我意识到我必须用 \ 开始正则表达式,然后是分隔符。
它如何仅适用于第一场比赛?很明显,它在匹配后附加文本,但它如何只知道第一个匹配?
对我来说,这会在每场比赛后添加文本。
有没有办法保持意图?
S
Stephane Chazelas

请注意标准的 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、cshrc 系列的 shell 中,'...' 是强引号,其中反斜杠并不特殊。我知道的唯一例外是 fish shell。
显然,MacOS 上的终端(以及随后在终端中运行的脚本)也是一个例外。我找到了替代语法,但无论如何感谢。
@AbandonedCart,那是另一回事。那是 macOS sed 在这里不符合 POSIX。这使得我关于它是可移植的声明不正确(对于单行变体)。如果我确实不符合标准或对标准的误解,我会要求 opengroup 确认。
我在使用不同版本的 sed 时遇到了很多问题,因此我尝试了 printf...ex 版本。这就像魅力!
S
SLePort

使用 s 命令的 POSIX 兼容:

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

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

在 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' 被 shell 评估为换行符的优势,它不在常规的 '\n' 单引号值中。

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


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

对此发布答案可能有点晚,但我发现上述一些解决方案有点麻烦。

我在 sed 中尝试了简单的字符串替换,它起作用了:

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

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

如前所述,如果您想就地执行此操作:

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

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

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

希望这可以帮助某人


这在默认使用 GNU sed 的 Linux 上运行良好,因为它理解替换字符串中的 \n。 Plain-vanilla (POSIX) sed 确实理解替换字符串中的 \n,因此这在 BSD 或 macOS 上不起作用——除非您以某种方式安装了 GNU sed。使用 vanilla sed,您可以通过“转义”换行来嵌入换行——也就是说,用反斜杠结束行,然后按 [Enter](标题下的 reference [2addr]s/BRE/replacement/flags)。这意味着您的 sed 命令必须跨越终端中的多行。
在替换字符串中获取文字换行符的另一个选项是使用 Bash 中的 ANSI-C quoting 功能,如 one of the other answers 中所述。
C
Corentin Limier

awk 变体:

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

对于任何想了解 1; 的人,请参阅 Why does “1” in awk print the current line?
C
Caleb

我有一个类似的任务,并且无法使上述 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

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


m
momonari8

我最近也必须为 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”的新行。这两个命令中的每一个都可以扩展为更复杂的操作和匹配。