我想在任何现有的#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/。

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

其他回答

作为一种替代建议,您可能希望查看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 script to change "foo" to "bar" only on the first occurrence
 1{x;s/^/first/;x;}
 1,/foo/{x;/first/s///;x;s/foo/bar/;}
 #---end of script---

或者,如果你喜欢:编辑注:只适用于GNU sed。

sed '0,/foo/s//bar/' file 

相当全面的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

你可以使用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更容易理解:-)

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

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/。

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