找到一条线上的两个模式并删除它们之间的模式和顺序

找到一条线上的两个模式并删除它们之间的模式和顺序

我有一个包含如下行的文件:

ABCDABCBCBBBCBCDDBBBBBBBBBBBBBBBBBBBBBBBBBXYZ
ABCDCCCBCCBBBBBBBBBBBBBBBBBBBBBBXYZ
ABCDACDCDCCCCBBBBBBBBBBBBBBBBBBBBBBBBXYZ

而且我要

BBBBBBBBBBBBBBBBBBBBBXYZ
BBBBBBBBBBBBBBBBBBXYZ
BBBBBBBBBBBBBBBBBBBBXYZ

因此,目标是删除从ABCD4 个连续 s 的第一次出现开始的所有内容(包括第一次出现的内容)B。保证所有行都以 开头,并且在结束之前的 s的“相关”链之前,该行上ABCD不能有“杂散” 。BBBBBXYZ

我尝试过sed与此接近的变体:

sed 's/ABCD.*BBBB//g' filename 

我认为这给了我XYZ,因为它直到最后一个模式才停止BBBB,但我希望它在第一个模式之后停止。

任何帮助将不胜感激!!

答案1

sed正如您所怀疑的,您的方法失败的原因是它sed基于正则表达式,并且这些是“贪婪的”,即它们尝试匹配它们可以描述的最长的可能字符串。

所以,这可能是 的任务awk。考虑这个程序:

awk '{n=index($0,"BBBB"); print substr($0,n+4)}' input.txt

BBBB这将定位当前行中第一次出现的子字符串(用 表示$0)并将位置存储在 中n。然后它将打印从该位置开始的行部分加上 4(以删除最初的 4B秒)直到行尾。

请注意,此处没有提及起始模式,ABCD因为您的示例输入表明全部行以 开头ABCD,在这种情况下,删除从行开头到(包括第一个 4- -)模式的所有内容就足够了B。如果假设不正确,特别是如果BBBB可以发生在 之前ABCD,它将无法按预期工作。

答案2

做你该做的事对于 ( remove everything starting with the ABCD up to, and including, the first occurence of 4 consecutive Bs.) 与任何 awk 都将是:

$ awk -v beg='ABCD' -v end='BBBB' '
    { gsub(end,"\n") }
    match($0,beg"[^\n]+\n") { $0=substr($0,1,RSTART-1) substr($0,RSTART+RLENGTH) }
    { gsub(/\n/,end) }
1' file
BBBBBBBBBBBBBBBBBBBBBXYZ
BBBBBBBBBBBBBBBBBBXYZ
BBBBBBBBBBBBBBBBBBBBXYZ

无论 ABCD 是行中的第一个还是 BBBB 可以出现在它之前,这都有效:

$ echo 'xyz BBBB foo ABCD bar BBBB etc BBBB anon' |
    awk -v beg='ABCD' -v end='BBBB' '{gsub(end,"\n")} match($0,beg"[^\n]+\n"){$0=substr($0,1,RSTART-1) substr($0,RSTART+RLENGTH)} {gsub(/\n/,end)} 1'
xyz BBBB foo  etc BBBB anon

答案3

如果序列仅出现一次BBBB*,则可以指示sed仅删除BBBB前面的第一个序列其他特点。

sed 's/^ABCD.*[^B]BBBB//'

如果BBBB序列每行只开始一次,那么应该可以完成工作。

请注意,它不适用于以下字符串:

ABCDEBBBBFBBBBXYZ

因为这是两次出现的情况BBBB,前面有非 B,贪心算法也会捕获第二次。

答案4

问题在于sed的正则表达式是“贪婪的”(即它们尝试尽可能多地匹配)。 sed没有用于匹配的非贪婪量词,但perl有 - 只需附加?在您要匹配的内容之后。例如

$ sed 's/ABCD.*BBBB//g' input.txt 
XYZ
XYZ
XYZ
$ perl -p -e 's/ABCD.*?BBBB//g' input.txt 
BBBBBBBBBBBBBBBBBBBBBXYZ
BBBBBBBBBBBBBBBBBBXYZ
BBBBBBBBBBBBBBBBBBBBXYZ

顺便说一句,像您这样的大多数简单脚本都可以使用代替(或在适当的情况下使用语句,而不是命令)sed来运行- 但使用perl正则表达式而不是perl -p -esedperl -n -eprintsed -np布雷(sed 的默认值)或 ERE ( sed -E)。请注意,与 不同sed-e指示下一个参数是脚本的 对于 来说不是可选的perl

man perlre

默认情况下,量化子模式是“贪婪的”,也就是说,它将匹配尽可能多的次数(给定特定的起始位置),同时仍然允许模式的其余部分匹配。如果您希望它匹配尽可能少的次数,请在量词后面加上?。请注意,含义没有改变,只是“贪婪”:

*?        Match 0 or more times, not greedily
+?        Match 1 or more times, not greedily
??        Match 0 or 1 time, not greedily
{n}?      Match exactly n times, not greedily (redundant)
{n,}?     Match at least n times, not greedily
{n,m}?    Match at least n but not more than m times, not greedily

相关内容