如何使用sed仅替换文件中的第一个匹配项?

时间:2020-03-06 14:52:18  来源:igfitidea点击:

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

我如何只替换文件中第一次出现的字符串而不替换每次出现的字符串?

如果我用

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

它替换所有的#includes。

也欢迎实现相同目标的其他建议。

解决方案

# 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---

或者,如果我们愿意:编者注:仅与GNUsed一起使用。

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

来源

我们可以使用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",我们需要转义引号。然后,将完成的变量设置为1,因此我们不会添加更多包含。

1;

这意味着"打印出该行",默认为空操作将打印$ 0,这将打印出整行。一个衬里并且比sed IMO更容易理解:-)

只需在末尾添加出现次数:

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

可能的解决方案:

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

解释:

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

#!/bin/sed -f
1,/^#include/ {
    /^#include/i\
#include "newfile.h"
}

该脚本的工作方式:对于1到第一个" #include"(在第1行之后)之间的行,如果该行以" #include"开头,则在指定的行之前添加。

但是,如果第一个" #include"在第1行中,则第1行和下一个后续的" #include"都将在该行之前。如果我们使用的是GNUsed,它具有扩展名,其中0,/ ^#include /(而不是1,)将做正确的事情。

我会用awk脚本来做到这一点:

BEGIN {i=0}
(i==0) && /#include/ {print "#include \"newfile.h\""; i=1}
{print 
awk -f awkscript headerfile.h > headerfilenew.h
} END {}

然后用awk运行它:

sed '0,/pattern/s/pattern/replacement/' filename

可能草率,我是新来的。

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

这对我有用。

例子

##代码##

编者注:两者都仅与GNUsed一起使用。