我想在任何现有的#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。
我们也欢迎为达到同样目的而提出的其他建议。
当前回答
我将提出一个建议,这并不完全是最初的问题所要求的,但对于那些还想特别替换匹配的第二次出现,或任何其他特别枚举的正则表达式匹配的人来说。使用python脚本和for循环,如果需要从bash脚本调用它。这是它看起来像我,在我替换特定行包含字符串-项目:
def replace_models(file_path, pixel_model, obj_model):
# find your file --project matches
pattern = re.compile(r'--project.*')
new_file = ""
with open(file_path, 'r') as f:
match = 1
for line in f:
# Remove line ending before we do replacement
line = line.strip()
# replace first --project line match with pixel
if match == 1:
result = re.sub(pattern, "--project='" + pixel_model + "'", line)
# replace second --project line match with object
elif match == 2:
result = re.sub(pattern, "--project='" + obj_model + "'", line)
else:
result = line
# Check that a substitution was actually made
if result is not line:
# Add a backslash to the replaced line
result += " \\"
print("\nReplaced ", line, " with ", result)
# Increment number of matches found
match += 1
# Add the potentially modified line to our new file
new_file = new_file + result + "\n"
# close file / save output
f.close()
fout = open(file_path, "w")
fout.write(new_file)
fout.close()
其他回答
你可以使用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更容易理解:-)
相当全面的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
POSIXly(在sed中也有效),只使用一个正则表达式,只需要一行内存(和往常一样):
sed '/\(#include\).*/!b;//{h;s//\1 "newfile.h"/;G};:1;n;b1'
解释道:
sed '
/\(#include\).*/!b # Only one regex used. On lines not matching
# the text `#include` **yet**,
# branch to end, cause the default print. Re-start.
//{ # On first line matching previous regex.
h # hold the line.
s//\1 "newfile.h"/ # append ` "newfile.h"` to the `#include` matched.
G # append a newline.
} # end of replacement.
:1 # Once **one** replacement got done (the first match)
n # Loop continually reading a line each time
b1 # and printing it by default.
' # end of sed script.
我终于得到了这个工作在一个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脚本中使用它。
没什么新内容,只是可能有一些更具体的答案:sed -rn '0,/foo(bar)。* / % % \ 1% p '
示例:xwininfo -name unit -launcher输出如下:
xwininfo: Window id: 0x2200003 "unity-launcher"
Absolute upper-left X: -2980
Absolute upper-left Y: -198
Relative upper-left X: 0
Relative upper-left Y: 0
Width: 2880
Height: 98
Depth: 24
Visual: 0x21
Visual Class: TrueColor
Border width: 0
Class: InputOutput
Colormap: 0x20 (installed)
Bit Gravity State: ForgetGravity
Window Gravity State: NorthWestGravity
Backing Store State: NotUseful
Save Under State: no
Map State: IsViewable
Override Redirect State: no
Corners: +-2980+-198 -2980+-198 -2980-1900 +-2980-1900
-geometry 2880x98+-2980+-198
使用xwininfo -name unit -launcher|sed -rn '0提取窗口ID,/^xwininfo:窗口ID: (0x[0-9a- fa -f]+)。*/ s%%\1%p'产生:
0x2200003