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

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

如果我用

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

它替换所有#includes。

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


当前回答

作为一种替代建议,您可能希望查看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

其他回答

我终于得到了这个工作在一个Bash脚本用来插入一个唯一的时间戳在每个项目在RSS提要:

        sed "1,/====RSSpermalink====/s/====RSSpermalink====/${nowms}/" \
            production-feed2.xml.tmp2 > production-feed2.xml.tmp.$counter

它只改变第一个事件。

${nowms}是Perl脚本设置的以毫秒为单位的时间,$counter是脚本中用于循环控制的计数器,\允许命令在下一行继续执行。

文件被读入,stdout被重定向到工作文件。

我理解它的方式,1,/====RSSpermalink====/告诉sed什么时候通过设置范围限制来停止,然后s/====RSSpermalink====/${nowms}/是熟悉的sed命令,用第二个字符串替换第一个字符串。

在我的例子中,我把命令放在双引号中,因为我在一个带有变量的Bash脚本中使用它。

使用FreeBSD ed,避免ed在文件中没有include语句时出现“no match”错误:

teststr='
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
'

# using FreeBSD ed
# to avoid ed's "no match" error, see
# *emphasized text*http://codesnippets.joyent.com/posts/show/11917 
cat <<-'EOF' | sed -e 's/^ *//' -e 's/ *$//' | ed -s <(echo "$teststr")
   H
   ,g/# *include/u\
   u\
   i\
   #include "newfile.h"\
   .
   ,p
   q
EOF

相当全面的linuxtopia常见问题解答集合。它还强调了人们提供的一些答案不能与非gnu版本的sed一起工作,例如

sed '0,/RE/s//to_that/' file

在非gnu版本中必须是

sed -e '1s/RE/to_that/;t' -e '1,/RE/s//to_that/'

但是,这个版本不能与gnu sed一起工作。

下面是一个两者都适用的版本:

-e '/RE/{s//to_that/;:a' -e '$!N;$!ba' -e '}'

ex:

sed -e '/Apple/{s//Banana/;:a' -e '$!N;$!ba' -e '}' filename

如果有人来这里替换所有行中第一次出现的字符(比如我自己),使用这个:

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。

sed -e 's/pattern/REPLACEMENT/1' <INPUTFILE