sed:如何删除文件中的第二个匹配项
sed: How to delete second match in a file
我有一个看起来像这样的文件(伪代码):
---
foo: bar
bar: baz
---
baz: quz
---
Some text
Some text
Some text
我需要删除 second ---
行,仅此而已。我知道 sed
可以做到这一点,但我从来没有能够从我能找到的任何 sed
文档中得出正面或反面...
见Sed replace every nth occurrence
该解决方案使用 awk 而不是 sed,但是 "use the right tool for the job"。在 sed 中可能会也可能不会,但即使可以,在 awk 或 perl 等工具中也会容易得多。
使用 sed 最简单的方法是首先将整个文件读入模式 space 并进行处理:
sed ':a $!{N; ba}; s/\(^\|\n\)---\n/\n/2' filename
这确实
:a # jump label for looping
$!{ # if the end of input is not reached
N # fetch the next line, append it to the pattern space
ba # go back to :a
} # after this, the whole file is in the pattern space.
s/\(^\|\n\)---\n/\n/2 # then: remove the second occurrence of a line that
# consists only of ---
@mklement0 指出 \|
仅适用于 GNU sed。一种解决方法,因为 \|
只需要在第一行中捕获 ---
,所以
sed ':a $!{ N; ba; }; s/^/\n/; s/\n---\n/\n/2; s/^\n//' filename
这样做:
:a $!{ N; ba; } # read file into the pattern space
s/^/\n/ # insert a newline before the first line
s/\n---\n/\n/2 # replace the second occurrence of \n---\n with \n
s/\n// # remove the newline we put in at the beginning.
这样第一行就不是特例了[=20=]
如果不将整个文件读入缓冲区,您将不得不从字符构造一个计数器:
sed '/^---$/ { x; s/.*/&_/; /^__$/ { x; d; }; x; }' filename
即:
/^---$/ { # if a line is ---
x # exchange pattern space and hold buffer
s/.*/&_/ # append a _ to what was the hold buffer
/^__$/ { # if there are exactly two in them
x # swap back
d # delete the line
}
x # otherwise just swap back.
}
...或者只使用 awk:
awk '!/^---$/ || ++ctr != 2' filename
sed 用于在一行中进行简单替换。对于其他任何事情,您应该只使用 awk:
$ awk '!(/^---$/ && ++cnt==2)' file
---
foo: bar
bar: baz
baz: quz
---
Some text
Some text
Some text
这是一些意大利面条式 sed 代码(使用 goto
)
sed '/^---/ {:a;n;/^---/{d;bb};ba;:b}' file
有评论
sed '/^---/ { # at the first match
:a # label "a"
n # get the next line of input
/^---/{d;bb} # if it matches, delete the line and goto "b"
ba # branch to "a" (goto)
:b # label "b"
}' file
但我要补充一点,我认为对任何复杂的事情使用 sed 都会导致代码无法维护。使用 awk 或 perl。感谢您有机会炫耀 ;)
这可能适合您 (GNU sed):
sed '/^---/{x;s/^/n/;/^n\{2\}$/{x;d};x}' file
在货舱中制作计数器space。每次遇到一行开头 ---
时,将计数器加一,如果计数器是 2,则删除当前行。
我有一个看起来像这样的文件(伪代码):
---
foo: bar
bar: baz
---
baz: quz
---
Some text
Some text
Some text
我需要删除 second ---
行,仅此而已。我知道 sed
可以做到这一点,但我从来没有能够从我能找到的任何 sed
文档中得出正面或反面...
见Sed replace every nth occurrence
该解决方案使用 awk 而不是 sed,但是 "use the right tool for the job"。在 sed 中可能会也可能不会,但即使可以,在 awk 或 perl 等工具中也会容易得多。
使用 sed 最简单的方法是首先将整个文件读入模式 space 并进行处理:
sed ':a $!{N; ba}; s/\(^\|\n\)---\n/\n/2' filename
这确实
:a # jump label for looping
$!{ # if the end of input is not reached
N # fetch the next line, append it to the pattern space
ba # go back to :a
} # after this, the whole file is in the pattern space.
s/\(^\|\n\)---\n/\n/2 # then: remove the second occurrence of a line that
# consists only of ---
@mklement0 指出 \|
仅适用于 GNU sed。一种解决方法,因为 \|
只需要在第一行中捕获 ---
,所以
sed ':a $!{ N; ba; }; s/^/\n/; s/\n---\n/\n/2; s/^\n//' filename
这样做:
:a $!{ N; ba; } # read file into the pattern space
s/^/\n/ # insert a newline before the first line
s/\n---\n/\n/2 # replace the second occurrence of \n---\n with \n
s/\n// # remove the newline we put in at the beginning.
这样第一行就不是特例了[=20=]
如果不将整个文件读入缓冲区,您将不得不从字符构造一个计数器:
sed '/^---$/ { x; s/.*/&_/; /^__$/ { x; d; }; x; }' filename
即:
/^---$/ { # if a line is ---
x # exchange pattern space and hold buffer
s/.*/&_/ # append a _ to what was the hold buffer
/^__$/ { # if there are exactly two in them
x # swap back
d # delete the line
}
x # otherwise just swap back.
}
...或者只使用 awk:
awk '!/^---$/ || ++ctr != 2' filename
sed 用于在一行中进行简单替换。对于其他任何事情,您应该只使用 awk:
$ awk '!(/^---$/ && ++cnt==2)' file
---
foo: bar
bar: baz
baz: quz
---
Some text
Some text
Some text
这是一些意大利面条式 sed 代码(使用 goto
)
sed '/^---/ {:a;n;/^---/{d;bb};ba;:b}' file
有评论
sed '/^---/ { # at the first match
:a # label "a"
n # get the next line of input
/^---/{d;bb} # if it matches, delete the line and goto "b"
ba # branch to "a" (goto)
:b # label "b"
}' file
但我要补充一点,我认为对任何复杂的事情使用 sed 都会导致代码无法维护。使用 awk 或 perl。感谢您有机会炫耀 ;)
这可能适合您 (GNU sed):
sed '/^---/{x;s/^/n/;/^n\{2\}$/{x;d};x}' file
在货舱中制作计数器space。每次遇到一行开头 ---
时,将计数器加一,如果计数器是 2,则删除当前行。