出于某种原因,我似乎无法找到一个直接的答案,而且我现在有点时间紧张。我将如何使用 sed
命令在匹配特定字符串的第一行之后插入选择的文本行。我有 ...
CLIENTSCRIPT="foo"
CLIENTFILE="bar"
我想在 CLIENTSCRIPT=
行之后插入一行,导致...
CLIENTSCRIPT="foo"
CLIENTSCRIPT2="hello"
CLIENTFILE="bar"
尝试使用 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(附加)
请注意标准的 sed
语法(如在 POSIX 中,所有符合 sed
的实现都支持(GNU、OS/X、BSD、Solaris...)):
sed '/CLIENTSCRIPT=/a\
CLIENTSCRIPT2="hello"' file
或者在一行:
sed -e '/CLIENTSCRIPT=/a\' -e 'CLIENTSCRIPT2="hello"' file
(-e
xpressions(和 -f
iles 的内容)与换行符连接起来组成 sed 脚本 sed
解释)。
用于就地编辑的 -i
选项也是 GNU 扩展,其他一些实现(如 FreeBSD 的)支持 -i ''
。
或者,为了可移植性,您可以改用 perl
:
perl -pi -e '$_ .= qq(CLIENTSCRIPT2="hello"\n) if /CLIENTSCRIPT=/' file
或者您可以使用 ed
或 ex
:
printf '%s\n' /CLIENTSCRIPT=/a 'CLIENTSCRIPT2="hello"' . w q | ex -s file
sed -e '/CLIENTSCRIPT=/a\' -e 'CLIENTSCRIPT2="hello"' file
转义了第一个参数末尾的引号并破坏了命令。
csh
或 rc
系列的 shell 中,'...'
是强引号,其中反斜杠并不特殊。我知道的唯一例外是 fish
shell。
sed
在这里不符合 POSIX。这使得我关于它是可移植的声明不正确(对于单行变体)。如果我确实不符合标准或对标准的误解,我会要求 opengroup 确认。
printf...ex
版本。这就像魅力!
使用 s
命令的 POSIX 兼容:
sed '/CLIENTSCRIPT="foo"/s/.*/&\
CLIENTSCRIPT2="hello"/' file
sed -e '/.../s/$/\' -e 'CLI.../'
,这将避免包含不形成有效字符的字节序列的行出现问题。
在 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 可能会因为更具可读性而获胜,但它已经足够长了。
s/CLIENTSCRIPT="foo"/&\
[Enter] CLIENTSCRIPT2="hello"/
。反斜杠必须是该行的最后一个字符(后面没有空格),这与它在此注释中的外观相反。
s///
替换字符串中的换行符以及 a
和 i
命令中的换行符可以使用我在上面评论中描述的相同方法“转义”。
对此发布答案可能有点晚,但我发现上述一些解决方案有点麻烦。
我在 sed 中尝试了简单的字符串替换,它起作用了:
sed 's/CLIENTSCRIPT="foo"/&\nCLIENTSCRIPT2="hello"/' file
& 符号反映匹配的字符串,然后添加 \n 和新行。
如前所述,如果您想就地执行此操作:
sed -i 's/CLIENTSCRIPT="foo"/&\nCLIENTSCRIPT2="hello"/' file
另一件事。您可以使用表达式进行匹配:
sed -i 's/CLIENTSCRIPT=.*/&\nCLIENTSCRIPT2="hello"/' file
希望这可以帮助某人
\n
。 Plain-vanilla (POSIX) sed
确实不理解替换字符串中的 \n
,因此这在 BSD 或 macOS 上不起作用——除非您以某种方式安装了 GNU sed。使用 vanilla sed,您可以通过“转义”换行来嵌入换行——也就是说,用反斜杠结束行,然后按 [Enter](标题下的 reference [2addr]s/BRE/replacement/flags
)。这意味着您的 sed 命令必须跨越终端中的多行。
awk 变体:
awk '1;/CLIENTSCRIPT=/{print "CLIENTSCRIPT2=\"hello\""}' file
1;
的人,请参阅 Why does “1” in awk print the current line?
我有一个类似的任务,并且无法使上述 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
行。
我最近也必须为 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”的新行。这两个命令中的每一个都可以扩展为更复杂的操作和匹配。
sed
。这不是标准的sed
语法,并且不适用于任何其他sed
实现。