gpt4 book ai didi

awk - 如果行与“foo”匹配,线上方与“bar”匹配,线下方与“baz”匹配,则删除行?

转载 作者:行者123 更新时间:2023-12-04 22:09:48 28 4
gpt4 key购买 nike

使用sed和/或awk,仅在行包含字符串“ foo”并且行之前和之后的行分别包含字符串“ bar”和“ baz”时,我才希望删除行。

因此,对于此输入:

blah
blah
foo
blah
bar
foo
baz
blah


我们将删除第二个foo,但没有别的,留下:

blah
blah
foo
blah
bar
baz
blah


我尝试使用while循环逐行读取文件,但这很慢,而且我不知道如何匹配上一行和下一行。

编辑-根据评论的要求,这是我的while循环的当前状态。当前仅匹配上一行(从上一个循环存储为$ linepre)。

linepre=0 
while read line
do
if [ $line != foo ] && [ $linepre != bar ]
then
echo $line
fi
linepre=$line
done < foobarbaz.txt


真是丑。

最佳答案

有关优雅的perl解决方案,请参见Sundeep's answer

有关类似且非常好的sed解决方案,请参见potong's second answer

两种解决方案都将文件完全读取到内存中并一次性处理。如果您不需要处理GB文件大小,那就很好。换句话说,这是最好的解决方案(如果我们忽略CASE3)。

注释:两种解决方案均CASE3失败(请参见下文)。 CASE3是一个值得商de的例外情况。



更新1:以下awk解决方案是一个在所有情况下均适用的新脚本。在某些情况下,早已接受此答案的早期解决方案失败了。提出的解决方案解决了嵌套分组(以下为CASE3):

awk 'BEGIN{p=1;l1=l2=""}
(NR>2) && p {print l1}
{ p=!(l1~/bar/&&l2~/foo/&&/baz/);
l1=l2;l2=$0
}
END{if (l1!="" && p) print l1
if (l2!="" ) print l2}' <file>


为了解决该问题,我们不断缓冲存储在 l1l2$0中的3行。每次处理新行时,我们确定是否应在下一个循环中打印 l1并交换缓冲的行。打印仅从 NR=3开始。要打印的条件是,如果 l1包含 barl2包含 foo,并且 $0包含 baz,则我们不在下一个循环中打印。

更新2:可以获得基于相同原理的 sed解决方案。 sed有两个记忆。模式空间是您进行所有操作的地方,保持空间是长期的内存。想法是将单词 print放在保留空间中,但是我们只能通过交换周围的空格(使用 x)来做到这一点

 sed '1{x;s/^.*$/print/;x;N};                           #1
N; #2
x;/print/{z;x;P;x};x; #3
/bar.*\n.*foo.*\n.*baz/!{x;s/^.*$/print/;x}; #4
$s/\(bar.*\)\n.*foo.*\n\(.*baz\)/\1\n\2/; #5
D' <file> #6



#1行通过将单词 print放置在保持空间( x;s...;x)中来初始化状态,并将另一行追加到模式空间( N
#2行将第三行添加到模式空间
#3行通过检查保留空间来确定是否需要打印图案空间的第一行,并删除保留空间。 P打印到图案空间中的第一个 \n,然后 z改变图案空间
#4行确定是否应在下一个周期中打印。检查真实模式是否匹配,如果不匹配,请在保持空间中放置单词 print
#5行是文件结束条件
#6删除模式空间中的第一个 \n,并返回到 #1,而不读取新行。


在出口处,再次打印图案空间。

注释:如果要查看模式空间和保留空间的外观,可以在每行之后添加以下代码: s/^/P:/;l;s/^P://;x;s/^/H:/;l;s/^H://;x。该行将同时打印两个空格,并分别在前面加上 P:H:

二手测试文件:

# bar-foo-baz test file
# An asterisk indicates the foo
# lines that should be removed
<CASE0 :: default case>
bar
foo (*)
baz
<CASE1 :: reset cycle on second line>
bar
foobar
foo (*)
baz
<CASE2 :: start cycle at end of previous cycle>
bar
foo (*)
bazbar
foo (*)
baz
<CASE3 :: nested cases>
bar
foobar (*)
foobaz (*)
baz
<CASE4 :: end-of-file case>
bar
foo


先前接受的答案:(已更新以指示失败的案例)

awk:失败 CASE3

awk '!/baz/&&(c==2){print foo}
/bar/ {c=1;print;next}
/foo/ &&(c==1){c++;foo=$0;next}
{c=0;print}
END{if(c==2){print foo}}' <file>


此解决方案默认情况下会打印所有行,除非该行包含 foo,该行在包含 bar的行之后。上面的逻辑仅决定我们是否应该打印行 foo


!/baz/&&(c==2){print foo}:这解决了提前终止的问题。如果在有效的 baz组合后未找到 bar-foo,它将打印 foo行。
/bar/{c=1;print;next}:这将初始化新循环的开始。如果找到 bar,请将 c设置为 1,打印该行并移至下一行。 bar行始终被打印。此行解析 CASE1CASE2
/foo/&&(c==1){c++;foo=$0;next}:这检查 bar-foo组合。它存储 foo行并移至下一行。
{c=0;print},如果我们达到了这一点,则意味着我们没有找到 bar行或 bar-foo组合。只需默认打印该行,并将计数器重置为零即可。
END{if(c==2){print foo}}该语句仅解决 CASE4


gawk:失败 CASE3

awk 'BEGIN{ORS="";RS="bar[^\n]*\n[^\n]*foo[^\n]*\n[^\n]*baz"}
{sub(/\n[^\n]*foo[^\n]*\n/,"\n",RT); print $0 RT}' <file>


RS设置为 bar[^\n]*\n[^\n]*foo[^\n]*\n[^\n]*baz,即我们感兴趣的模式。在这里, [^\n]*\n[^\n]*表示包含单个 \n的字符串,因此 RS表示有效的 bar-foo-baz组合。使用 RT编辑找到的记录分隔符 sub以删除 foo行,并在找到的记录之后打印。


RT(gawk扩展名)与由表示的文本匹配的输入文本
RS,记录分隔符。每次读取记录时设置。


sed:失败 CASE1, CASE2, CASE3, CASE4

sed -n '/bar/{N;/\n.*foo/{N;/foo.*\n.*baz[^\n]*$/{s/\n.*foo.*\n/\n/}}};p' <file>



/bar/{N;...}如果该行包含 bar,则将下一行追加到模式缓冲区( N
/\n.*foo/{N;...}如果模式缓冲区的换行符后有 foo,则将下一行追加到模式缓冲区( N
/foo.*\n.*baz[^\n]*$/{s/\n.*foo.*\n/\n/}如果模式缓冲区包含 foo,后跟一个换行符,并以包含 baz的行结尾,则删除包含 foo的行。此处的搜索模式将个案排除为 barfoo\nfoobaz\ncar

关于awk - 如果行与“foo”匹配,线上方与“bar”匹配,线下方与“baz”匹配,则删除行?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49112877/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com