我想在任何现有的#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。
我们也欢迎为达到同样目的而提出的其他建议。
当前回答
如果有人来这里替换所有行中第一次出现的字符(比如我自己),使用这个:
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 -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
sed '0,/pattern/s/pattern/replacement/' filename
这对我很管用。
例子
sed '0,/<Menu>/s/<Menu>/<Menu><Menu>Sub menu<\/Menu>/' try.txt > abc.txt
编者注:两者都只适用于GNU sed。
我将提出一个建议,这并不完全是最初的问题所要求的,但对于那些还想特别替换匹配的第二次出现,或任何其他特别枚举的正则表达式匹配的人来说。使用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()
#!/bin/sed -f
1,/^#include/ {
/^#include/i\
#include "newfile.h"
}
该脚本的工作原理:对于第1行和第一个#include(在第1行之后)之间的行,如果该行以#include开头,则在指定的行前面加上。
但是,如果第一个#include在第1行,那么第1行和下一个#include都将有该行前置。如果您正在使用GNU sed,它有一个扩展,其中0,/^#include/(而不是1,)将做正确的事情。
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.