我想在任何现有的#include之前,用一个额外的include指令更新大量的c++源文件。对于这类任务,我通常使用带有sed的小型bash脚本来重写文件。
我如何得到sed替换一个文件中字符串的第一次出现,而不是替换每一次出现?
如果我用
sed s/#include/#include "newfile.h"\n#include/
它替换所有#includes。
我们也欢迎为达到同样目的而提出的其他建议。
我想在任何现有的#include之前,用一个额外的include指令更新大量的c++源文件。对于这类任务,我通常使用带有sed的小型bash脚本来重写文件。
我如何得到sed替换一个文件中字符串的第一次出现,而不是替换每一次出现?
如果我用
sed s/#include/#include "newfile.h"\n#include/
它替换所有#includes。
我们也欢迎为达到同样目的而提出的其他建议。
当前回答
我知道这是一个老帖子,但我有一个解决方案,我过去经常使用:
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/。
即使第一次出现在第一行也有效。
其他回答
这可能为你工作(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脚本只将“Apple”第一次出现的位置替换为“Banana”
例子
Input: Output:
Apple Banana
Apple Apple
Orange Orange
Apple Apple
这是一个简单的脚本:编辑注:只适用于GNU sed。
sed '0,/Apple/{s/Apple/Banana/}' input_filename
前两个参数0和/Apple/是范围说明符。s/Apple/Banana/是在这个范围内执行的。因此,在这种情况下,“在Apple的开始(0)到第一个实例的范围内,将Apple替换为Banana。只有第一代苹果会被取代。
Background: In traditional sed the range specifier is also "begin here" and "end here" (inclusive). However the lowest "begin" is the first line (line 1), and if the "end here" is a regex, then it is only attempted to match against on the next line after "begin", so the earliest possible end is line 2. So since range is inclusive, smallest possible range is "2 lines" and smallest starting range is both lines 1 and 2 (i.e. if there's an occurrence on line 1, occurrences on line 2 will also be changed, not desired in this case). GNU sed adds its own extension of allowing specifying start as the "pseudo" line 0 so that the end of the range can be line 1, allowing it a range of "only the first line" if the regex matches the first line.
或者一个简化版本(空的RE,如//,意味着重用之前指定的,所以这是等价的):
sed '0,/Apple/{s//Banana/}' input_filename
对于s命令,花括号是可选的,所以这也是等价的:
sed '0,/Apple/s//Banana/' input_filename
所有这些工作都只在GNU上进行。
你也可以使用homebrew brew install GNU -sed在OS X上安装GNU sed。
下面的命令删除文件中字符串的第一次出现。它还删除了空行。它显示在xml文件上,但它可以用于任何文件。
如果您使用xml文件并且想要删除一个标记,则非常有用。在本例中,它删除了第一次出现的“isTag”标记。
命令:
sed -e 0,/'<isTag>false<\/isTag>'/{s/'<isTag>false<\/isTag>'//} -e 's/ *$//' -e '/^$/d' source.txt > output.txt
源文件:Source .txt
<xml>
<testdata>
<canUseUpdate>true</canUseUpdate>
<isTag>false</isTag>
<moduleLocations>
<module>esa_jee6</module>
<isTag>false</isTag>
</moduleLocations>
<node>
<isTag>false</isTag>
</node>
</testdata>
</xml>
结果文件(output.txt)
<xml>
<testdata>
<canUseUpdate>true</canUseUpdate>
<moduleLocations>
<module>esa_jee6</module>
<isTag>false</isTag>
</moduleLocations>
<node>
<isTag>false</isTag>
</node>
</testdata>
</xml>
ps:它在Solaris SunOS 5.10(相当旧)上不能工作,但在Linux 2.6 sed版本4.1.5上可以工作
我知道这是一个老帖子,但我有一个解决方案,我过去经常使用:
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/。
即使第一次出现在第一行也有效。
使用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|$)//。