使用sed在第一次匹配后插入行

时间:2013-03-21 22:23:35

标签: shell sed

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

CLIENTSCRIPT="foo"
CLIENTFILE="bar"

我希望在CLIENTSCRIPT=行之后插入一行,从而产生...

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

8 个答案:

答案 0 :(得分:330)

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

答案 1 :(得分:43)

请注意标准的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

或者您可以使用edex

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

答案 2 :(得分:12)

符合POSIX标准的s命令:

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

答案 3 :(得分:5)

Sed命令适用于MacOS(至少OS 10)和Unix一样(即不需要像Gilles(目前接受)那样的gnu sed):

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

这适用于bash,也可能是其他shell,它们知道$'\ n'评估报价样式。一切都可以在一条线上工作  old / 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解释,可能不会相同,就像{ {1}}是 - 例如。 \n将是匹配的第一行。特别方便的是,如果你要添加一个来自变量的行,你必须先使用$ {var //}或其他sed语句等来逃避所有内容。

这个解决方案在脚本中不那么混乱(引用和\ n不容易阅读),当你不想在行的开头放置a命令的替换文本时,如果说,在具有缩进线的函数中。我已经利用shell将$'\ n'评估为换行符,而不是常规'\ n'单引号值。

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

答案 4 :(得分:1)

awk变体:

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

答案 5 :(得分:0)

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

答案 6 :(得分:0)

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

我在sed中尝试了简单的字符串替换,并且有效:

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

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

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

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

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

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

希望这对某人有帮助

答案 7 :(得分: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”的新行。这两个命令中的每一个都可以扩展为更复杂的操作和匹配。