简体   繁体   English

sed 如果不在另一个匹配模式之后,则删除匹配模式的行

[英]sed remove a line matching a pattern if not after another matching pattern

The file contents should be like this:文件内容应该是这样的:

foo
foobar
bar

foo or foobar can exist alone. foo 或 foobar 可以单独存在。 But bar must be after foo and can't occur without foo.但是 bar 必须在 foo 之后并且没有 foo 就不能出现。 So any occurrences of bar without foo, I want to delete.因此,我想删除任何不带 foo 的 bar。

For my use case (the issue), bar was always coming before foo once and after foo once.对于我的用例(问题),bar 总是在 foo 之前和 foo 之后出现一次。

bar
...
foo
foobar
bar

So I used grep to find the number of occurrences and sed to delete the 1st occurrence of bar if there are more than one bar.因此,我使用grep查找出现的次数,如果有多个柱,则使用sed删除第一个出现的柱。

But I was wondering, is it possible with sed or some other tool to actually find the previous occurrence of foo and keep a count.但我想知道,是否有可能使用 sed 或其他工具来实际找到 foo 的先前出现并进行计数。 If it's not followed by a foo of its own, delete the bar.如果它后面没有自己的 foo,则删除该栏。 Like all the cases below.就像下面所有的案例一样。

1.(delete 1st bar) 1.(删除第一条)

bar
...
foo
foobar
bar

2.(delete 2nd bar because even though there is a foo before it, it's already counted for 1st bar) 2.(删除第二个小节,因为即使它前面有一个 foo,它也已经算作第一个小节了)

foo
foobar
bar
...
bar

3.(delete 2nd and 4th bar) 3.(删除第二和第四条)

foo
foobar
bar
...
bar
...
foo
bar
...
bar
awk '/^bar/{if (k) k=0; else next} /^foo/{k=1} 1' file

k means keep the line, it expires at first bar found after foo . k表示保留该行,它在foo之后找到的第一个bar到期。

is it possible with sed or some other tool to actually find the previous occurrence of foo and keep a count.是否有可能使用 sed 或其他工具来实际找到 foo 的先前出现并保持计数。

It is possible with sed , in principle, to keep a running count the number of appearances of foo .原则上,可以使用sed来计算foo出现的次数。 For example, performing an H command for each foo line would do this, though the resulting count would be in a form that was a bit tricky to use.例如,为每个foo行执行一个H命令就可以做到这一点,尽管结果计数的形式使用起来有点棘手。

But it sounds like you don't really need a count so much as you need a flag to report on whether any foo s have yet been seen.但听起来你并不需要一个计数,因为你需要一个标志来报告是否已经看到任何foo For that, I would just use an h command.为此,我只使用h命令。 An overall sed program to delete all bar lines that precede the first foo line, leaving all other lines unchanged, could look like this:总体sed程序删除第一行foo行之前的所有bar行,保持所有其他行不变,可能如下所示:

# When a 'foo' line is encountered, copy it to the hold space
/^foo$/ h
# If is a 'bar' then print or delete it, as appropriate
/^bar$/ {
# Append a newline and the contents of the hold space to the pattern space
G
# If the pattern space (now) ends in `foo`, then print up to the first newline of it
/foo$/ P
# delete the contents of the pattern space and start the next cycle
d
}

You can put that in a file and use sed 's -f option to read the commands from there, or you can put it directly on the command line by removing the comments and concatenating the lines with semicolon delimiters:您可以将它放在一个文件中,并使用sed-f选项从那里读取命令,或者您可以通过删除注释并用分号分隔符连接这些行来将其直接放在命令行中:

sed '/^foo$/ h; /^bar$/ {G; /foo$/ P; d; }' input > output

Update更新

Simpler, though, and clearer would be to express what you intend via an address range:不过,更简单、更清晰的方法是通过地址范围表达您的意图:

# For all lines from before the first through one containing foo
0,/^foo$/ {
# delete bar lines
/^bar$/ d
}

Or in a single command:或者在一个命令中:

sed '0,/^foo$/ { /^bar$/ d; }' input > output

However, use of line number 0 as an address or in an address range may require GNU sed .但是,使用行号 0 作为地址或在地址范围内可能需要 GNU sed It definitely works with GNU sed , but POSIX specifications for sed do not clearly indicate that it is supported.它绝对适用于 GNU sed ,但sed的 POSIX 规范并未明确表明它受支持。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM