我想在任何现有的#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。
我们也欢迎为达到同样目的而提出的其他建议。
当前回答
下面的命令删除文件中字符串的第一次出现。它还删除了空行。它显示在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上可以工作
其他回答
sed '0,/pattern/s/pattern/replacement/' filename
这对我很管用。
例子
sed '0,/<Menu>/s/<Menu>/<Menu><Menu>Sub menu<\/Menu>/' try.txt > abc.txt
编者注:两者都只适用于GNU sed。
你可以使用awk来做类似的事情。
awk '/#include/ && !done { print "#include \"newfile.h\""; done=1;}; 1;' file.c
解释:
/#include/ && !done
当行匹配“#include”并且我们还没有处理它时,在{}之间运行操作语句。
{print "#include \"newfile.h\""; done=1;}
这将打印#include "newfile.h",我们需要转义引号。然后我们将done变量设置为1,这样我们就不会添加更多的include。
1;
这意味着“打印出行”——一个空操作默认为打印$0,打印出整行。一个简单的程序,比sed更容易理解:-)
作为一种替代建议,您可能希望查看ed命令。
man 1 ed
teststr='
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
'
# for in-place file editing use "ed -s file" and replace ",p" with "w"
# cf. http://wiki.bash-hackers.org/howto/edit-ed
cat <<-'EOF' | sed -e 's/^ *//' -e 's/ *$//' | ed -s <(echo "$teststr")
H
/# *include/i
#include "newfile.h"
.
,p
q
EOF
如果有人来这里替换所有行中第一次出现的字符(比如我自己),使用这个:
sed '/old/s/old/new/1' file
-bash-4.2$ cat file
123a456a789a
12a34a56
a12
-bash-4.2$ sed '/a/s/a/b/1' file
123b456a789a
12b34a56
b12
例如,通过将1改为2,你可以只替换所有的第二个a。
我知道这是一个老帖子,但我有一个解决方案,我过去经常使用:
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/。
即使第一次出现在第一行也有效。