假设我有一个包含五百行文本的文件。
one
two
three
four
five
six
seven
eight
nine
ten
.
.
.
five-hundred
如果我想用某个字符串替换匹配后的第五行,我会这样做:
sed '/two/{n;n;n;n;n;s/.*/MODIFIED/}' inputfile
输出:
one
two
three
four
five
six
MODIFIED
eight
nine
ten
.
.
.
five-hundred
但是如果我想在比赛结束后替换第 60 行怎么办?我不想写六十次“n”。
我尝试使用 x、h/H、g/G 和范围,但我仍然无法获得所需的输出。
答案1
解决方法awk
:
awk '/two/{ n=NR+5 } NR==n{ sub(/.*/, "MODIFIED") }1' file
或者如果你想更换线路
awk '/two/{ n=NR+5 } NR==n{ $0="MODIFIED" }1' file
答案2
对于此类任务,您可能必须考虑如果搜索模式在偏移之前再次出现该怎么办。
awk -v offset=60 '
/two/ {x[NR + offset]}
NR in x {delete x[NR]; $0 = "MODIFIED"}
{print}'
每当上面的第 60 行包含 时,将确保替换一行two
。
答案3
/two/{
:loop
N
/\(\(.*\n\)\{5\}\).*/{
s//\1Modified/
b
}
b loop
}
将其保存x.sed
并执行为
sed -f x.sed file
或者使用 GNU Sed 的单行代码:
sed '/two/{:a;N;/\(\(.*\n\)\{5\}\).*/{s//\1Modified/;b;};ba;}' file
逐行分析:
- 1-2:当找到匹配项时,开始循环。在这个循环中,
- 3:使用命令向模式空间添加一条线
N
。 - 4:如果有 5 个换行符 — 基本正则表达式是
\(.*\n\)\{5\}
—,将其放入捕获组中,保留最后一行 (.*
) 不捕获,然后- 5:用捕获组替换整个模式空间*,后跟该
Modified
行。 - 6:打破循环。
- 5:用捕获组替换整个模式空间*,后跟该
- 8:再说
loop
一遍。
- 3:使用命令向模式空间添加一条线
*空的正则表达式槽相当于之前使用的正则表达式,因此第 5 行相当于s/\(\(.*\n\)\{5\}\).*/\1Modified/
.
不太深奥的是使用 Awk:
awk '{c--};/two/{c=5};c==0{$0="Modified"};{print}' file
首先是递减计数器c
,并且因为所有变量c
最初都是零,所以只有在找到匹配项时才会变为正数。
更明确的方法是
awk 'BEGIN{c=-1};/two/{c=5};c==0{$0="Modified"};{print};c>=0{c=c-1}' file
有用的 Sed 资源:
答案4
进行全行字符串匹配,并假设这样的匹配不会在 5 行范围内重复出现,在每个 Unix 机器上的任何 shell 中使用任何 awk:
$ awk 'c&&!--c{$0="MODIFIED"} $0=="two"{c=5} 1' file
one
two
three
four
five
six
MODIFIED
eight
nine
ten
.
.
.
five-hundred