我想在任何现有的#include之前,用一个额外的include指令更新大量的c++源文件。对于这类任务,我通常使用带有sed的小型bash脚本来重写文件。

我如何得到sed替换一个文件中字符串的第一次出现,而不是替换每一次出现?

如果我用

sed s/#include/#include "newfile.h"\n#include/

它替换所有#includes。

我们也欢迎为达到同样目的而提出的其他建议。


当前回答

没什么新内容,只是可能有一些更具体的答案:sed -rn '0,/foo(bar)。* / % % \ 1% p '

示例:xwininfo -name unit -launcher输出如下:

xwininfo: Window id: 0x2200003 "unity-launcher"

  Absolute upper-left X:  -2980
  Absolute upper-left Y:  -198
  Relative upper-left X:  0
  Relative upper-left Y:  0
  Width: 2880
  Height: 98
  Depth: 24
  Visual: 0x21
  Visual Class: TrueColor
  Border width: 0
  Class: InputOutput
  Colormap: 0x20 (installed)
  Bit Gravity State: ForgetGravity
  Window Gravity State: NorthWestGravity
  Backing Store State: NotUseful
  Save Under State: no
  Map State: IsViewable
  Override Redirect State: no
  Corners:  +-2980+-198  -2980+-198  -2980-1900  +-2980-1900
  -geometry 2880x98+-2980+-198

使用xwininfo -name unit -launcher|sed -rn '0提取窗口ID,/^xwininfo:窗口ID: (0x[0-9a- fa -f]+)。*/ s%%\1%p'产生:

0x2200003

其他回答

一个可能的解决方案:

    /#include/!{p;d;}
    i\
    #include "newfile.h"
    :a
    n
    ba

解释:

读取行,直到找到#include,打印这些行,然后开始新的循环 插入新的包含行 输入一个只读取行(默认情况下sed也会打印这些行)的循环,我们不会从这里回到脚本的第一部分

这可能为你工作(GNU sed):

sed -si '/#include/{s//& "newfile.h\n&/;:a;$!{n;ba}}' file1 file2 file....

或者如果内存不是问题:

sed -si ':a;$!{N;ba};s/#include/& "newfile.h\n&/' file1 file2 file...
sed '0,/pattern/s/pattern/replacement/' filename

这对我很管用。

例子

sed '0,/<Menu>/s/<Menu>/<Menu><Menu>Sub menu<\/Menu>/' try.txt > abc.txt

编者注:两者都只适用于GNU sed。

使用GNU sed的-z选项,您可以像处理一行文件一样处理整个文件。这样,s/…/…/将只替换整个文件中的第一个匹配项。记住:s/…/…/只替换每行中的第一个匹配项,但是使用-z选项sed将整个文件视为一行。

sed -z 's/#include/#include "newfile.h"\n#include'

在一般情况下,您必须重写sed表达式,因为模式空间现在包含整个文件,而不仅仅是一行。一些例子:

s /文本。*//可以重写为s/text[^\n]*//。[^\n]匹配除换行符以外的所有内容。[^\n]*将匹配文本后的所有符号,直到出现换行符。 S /^text//可以重写为S /(^|\n)text//。 S /text$//可以重写为S /text(\n|$)//。

我知道这是一个老帖子,但我有一个解决方案,我过去经常使用:

grep -E -m 1 -n 'old' file | sed 's/:.*$//' - | sed 's/$/s\/old\/new\//' - | sed -f - file

基本上使用grep打印第一次出现并在那里停止。另外,打印行号即5:行。将其导入sed并删除:和后面的所有内容,这样就只剩下行号了。将其导入sed,从而添加s/。*/replace到结束数字,这将导致一个1行脚本,该脚本被管道插入到最后一个sed中,作为脚本在文件上运行。

因此,如果regex = #include and replace = blah,并且grep发现的第一个出现在第5行,那么传输到最后一个sed的数据将是5s/.*/blah/。

即使第一次出现在第一行也有效。