如何在模式最后一次出现后删除所有行?

时间:2017-06-01 13:15:37

标签: bash awk sed

我希望在模式最后一次出现之后删除除模式本身之外的所有行

file.txt的

honor
apple
redmi
nokia
apple
samsung
lg
htc

file.txt我想要的是什么

honor
apple
redmi
nokia
apple

我尝试了什么

sed -i '/apple/q' file.txt

这会在第一次出现模式后删除所有行 -

honor

7 个答案:

答案 0 :(得分:6)

使用几乎没有内存的简单,强大的2遍方法:

$ awk 'NR==FNR{if (/apple/) hit=NR; next} {print} FNR==hit{exit}' file file
honor
apple
redmi
nokia
apple

如果执行速度不够快那么,是时候尝试一些替代方案,看看是否会产生性能提升。

答案 1 :(得分:3)

反转文件,从第一次出现的模式开始打印所有内容,然后反转结果:

tac file.txt | sed -n '/apple/,$p' | tac > newfile.txt

您可以找到最后一个匹配的行号,然后用它来打印文件的前N行:

line=$(awk '/apple/ { line=NR } END {print line}')
head -n $line file.txt > newfile.txt

答案 2 :(得分:1)

如果您不想像Barmar建议的那样反转文件,您将需要使用较低级别的工具(例如,fseek)反向读取文件或阅读两次:

sed $(awk '/apple/{a=NR}END{print a+1}' input),\$d input

(请注意,如果模式没有出现在文件中,则不会输出任何内容。这是您应该担心的边缘情况。)

答案 3 :(得分:1)

这可能适合你(GNU sed):

sed '/apple/,$!b;//!H;//{x;//p;x;h};${x;P};d' file

照常打印任何非apple首次出现到文件末尾的行。对于上述范围内的行,将不包含单词apple的行附加到保留空间(HS)。包含单词apple的行,首先交换到HS,如果单词apple在那里则打印任何行,然后将HS替换为包含apple的行。删除最后一行以外的所有行。在文件末尾打印HS的第一行并删除剩余的行。

如果啜饮大文件不是问题,请使用:

sed -rz 's/(.*apple[^\n]*).*/\1\n/' file

这使用贪婪来捕捉单词apple之前的所有行。

答案 4 :(得分:0)

这是另一个awk没有扫描文件两次

$ awk 'f       {buf=buf ORS $0} 
       /apple/ {f=1; if(buf)print buf; buf=$0} 
       !f' file

honor
apple
redmi
nokia
apple

答案 5 :(得分:0)

如果你不介意记忆中的一切,你可以这样做:

$ awk '/^apple$/{last=NR} 
              {lines[NR]=$0}
     END{for(li=1;li<=last;li++) print lines[li]}' file
honor
apple
redmi
nokia
apple

答案 6 :(得分:0)

鉴于您正在处理大量输入,我会使用两遍coreutils解决方案,例如:

n=$(grep -Fn apple infile | tail -n1 | cut -d: -f1)
[ -n "$n" ] && head -n$n infile > outfile

这使用 grep的固定字符串匹配(-F)来查找包含苹果的每一行。然后 head 用于提取相关行。

您未指定未找到苹果时会发生什么,因此此解决方案在发生时无效。