【问题标题】:sed/awk/perl remove the first two lines of a 3 line patternsed/awk/perl 删除 3 行模式的前两行
【发布时间】:2020-01-24 23:47:08
【问题描述】:

我有一个巨大的文本文件。我需要替换这三行的所有出现 模式:

|pattern|some data|
|giberish|,,
|pattern|some other data|

模式的最后一行:

|pattern|some other data|

删除模式的前两行,只保留最后一行

  • 模式的第二行以两个逗号结尾,不以|pattern|开头
  • 模式行的第一行以|pattern| 开头,不以两个逗号结尾。
  • 模式行的第三行以|pattern| 开头,不以两个逗号结尾。

我试过了:

sed 'N;N;/^|pattern|.*\n.*,,\n|pattern|.*/I,+1 d' trial.txt

运气不好

编辑:这是一个更重要的例子

#!/usr/bin/env bash
cat > trial.txt <<EOL
|pattern|sdkssd|
|.x,mz|e,dsa|,,
|pattern|sdk;sd|
|xl'x|cxm;s|,,
|pattern|aslkaa|
|l'kk|3lke|,,
|x;;lkaa|c,c,s|
|-0-ses|3dsd|
|xk;xzz|'l3ld|
|0=9c09s|klkl32|
|d0-zox|m,3,a|
|x'.za|wkl;3|
|=-0poxz|3kls|
|x-]0';a|sd;ks|
|wsd|756|
|sdw|;lksd|
|pattern|askjkas|
|xp]o]xa|lk3j2|,,
|]-p[z|lks|
EOL

它应该变成:

|pattern|aslkaa|
|l'kk|3lke|,,
|x;;lkaa|c,c,s|
|-0-ses|3dsd|
|xk;xzz|'l3ld|
|0=9c09s|klkl32|
|d0-zox|m,3,a|
|x'.za|wkl;3|
|=-0poxz|3kls|
|x-]0';a|sd;ks|
|wsd|756|
|sdw|;lksd|
|pattern|askjkas|
|xp]o]xa|lk3j2|,,
|]-p[z|lks|

@zdim:

文件的前三行:

|pattern|sdkssd|
|.x,mz|e,dsa|,,
|pattern|sdk;sd|

满足模式。所以他们被替换为

|pattern|sdk;sd|

所以文件的顶部现在变成了:

|pattern|sdk;sd|
|xl'x|cxm;s|,,
|pattern|aslkaa|
|l'kk|3lke|,,
...

其中的前三行是:

|pattern|sdk;sd|
|xl'x|cxm;s|,,
|pattern|aslkaa|

满足模式,所以替换为:

|pattern|aslkaa|

所以现在文件的顶部是:

|pattern|aslkaa|
|l'kk|3lke|,,
|x;;lkaa|c,c,s|
|-0-ses|3dsd|
....

@JosephQuinsey:

考虑这个文件:

#!/usr/bin/env bash
cat > trial.txt <<EOL
|pattern|blabla|
|||4|||-0.97|0|1429037262.8271||20160229||1025||1000.0|0.01|,,
|pattern|blable|
|||5|||-1.27|0|1429037262.854||20160229||1025||1000.0|0.01|,,
|pattern|blasbla|
|||493|||-0.22|5|1429037262.8676||20170228||1025||1000.0|0.01|,,
|||11|||-0.22|5|1429037262.8676||20170228||1025||1000.0|0.01|,|T|347||1429043438.1962|-0.22|5|0||-0.22|1429043438.1962|,|Q|346||1429043437.713|-0.24|26|-0.22|5|||1429043437.713|
|pattern|jksds|
|||232|||-5.66|0|1429037262.817||20150415||1025||1000.0|0.01|,,
|pattern|bdjkds|
|||123q|||-7.15|0|1429037262.8271||20150415||1025||1000.0|0.01|,,
|pattern|blabla|
|||239ps|||-1.38|79086|1429037262.8773||20150415||1025||1000.0|0.01|,,
|||-92opa|||-1.38|79086|1429037262.8773||20150415||1025||1000.0|0.01|,|T|1||1428969600.5019|-0.99|1|11||||,
|||kj2w|||-1.38|79086|1429037262.8773||20150415||1025||1000.0|0.01|,|T|2||1428969600.5019|-1|1|11||||,
|||0293|||-1.38|79086|1429037262.8773||20150415||1025||1000.0|0.01|,|T|3||1428969600.5019|-1.01|1|11||||,
|||2;;w32|||-1.38|79086|1429037262.8773||20150415||1025||1000.0|0.01|,|T|4||1428969600.5019|-1.11|1|11||||,
EOL

【问题讨论】:

  • 所以你的意思是用|pattern|still some other different data替换第3行还是你可以删除第1行和第2行吗?如果合适,请更新您的 Q。祝你好运。
  • @shellter:“删除模式的前两行”这句话在原始问题中。我已经强调了它。 !您只需要删除第 1 行和第 2 行!。为什么好运?这是一件复杂的事情吗?
  • '删除模式的前两行'似乎不那么模棱两可了。我签署了我所有的 cmets 祝你好运。即使是这个,祝你好运;-)!
  • 为什么投反对票?!
  • 所示示例以三行模式开头,最后一个是|pattern|sdk;sd|。所以我希望在输出中,但不是吗?现在,该行本身开始另一个模式,以您在所需输出中实际显示的内容结束。什么给了?

标签: perl ubuntu awk sed


【解决方案1】:

这是一个简单的例子,使用缓冲区来收集和管理模式线

use warnings;
use strict;
use feature 'say';

my $file = shift or die "Usage: $0 file\n";

open my $fh, '<', $file or die "Can't open $file: $!";

my @buf;

while (<$fh>) { 
    chomp;
    if (/^\|pattern\|/ and not /,,$/) { 
        @buf = $_;     # start the buffer (first line) or overwrite (third)
    }   
    elsif (/,,$/ and not /^\|pattern\|/) { 
        if  (@buf) { push @buf, $_ }  # add to buffer with first line in it
        else       { say }            # not part of 3-line-pattern; print
    }   
    else { 
        say for @buf;  # time to print out buffer
        @buf = ();     # ... empty it ...
        say            # and print the current line
    }   
}

这将打印预期的输出。

解释。

  • 模式行进入缓冲区,当我们得到“第三行”时,需要删除前两行。然后每当我们看到^|pattern| 时“分配”给数组——如果它是第一行则启动缓冲区,或者如果它是第三行则重新初始化数组(删除其中的内容)

  • ,, 结尾的行被添加到缓冲区中,如果那里已经有一行的话。没有什么可以禁止以,, 结尾的行——它们可能在模式之外;在这种情况下,只需打印它

  • 所以每条|pattern| 行都会直接设置缓冲区——要么启动它,要么重置它。因此,一旦我们遇到既没有^|pattern| 也没有,,$ 的行,我们就可以打印出我们的缓冲区,并且那行

请更全面地测试,我还没有做。


为了在管道或文件中运行它,请使用"magical" &lt;&gt; 文件句柄。于是就变成了

use warnings;
use strict;
use feature 'say';

my @buf;

while (<>) {  # reads lines from files given on command line, or from STDIN
    ...
}

现在您可以使用data | script.plscript.pl datafile 运行它。 (为此使脚本可执行,或用作perl script.pl。)

脚本的输出到STDOUT,可以通过管道传输到其他程序或重定向到文件。

【讨论】:

  • 抱歉,这是什么语言?在 bash 提示符下如何使用(假设文件名为 trial.txt)
  • @user189035 哦……这是一个 Perl 程序。你以scriptname.pl datafile 运行它,显示的程序在scriptname.pl 文件中(当然可以叫它任何名称),它是可执行的。 (或者你可以以perl scriptname.pl datafile 运行它)这个可以被按摩成一个“单线”,但这太复杂和混乱
  • 能否将其更改为包含在管道链中:... | perl perlscript.pl | ... &gt; trial2.txt?
  • @user189035 编辑正则表达式,禁止|pattern|,, 结尾,并禁止,,$|pattern| 开头。也许是挑剔,但这是您的要求。
  • 我确认它运行良好,比 tac 快 3 倍 |哇 |基于 tac 的解决方案。出于尊重,请允许我检查其他答案。
【解决方案2】:

这可能取决于您的文件有多大,但如果它小于允许的内存大小,如何:

perl -0777 -pe '
    1 while s/^\|pattern\|.+?\|\n(?<!\|pattern\|).+?,,\n(\|pattern\|.+?\|)$/\1/m;
' trial.txt

输出:

|pattern|aslkaa|
|l'kk|3lke|,,
|x;;lkaa|c,c,s|
|-0-ses|3dsd|
|xk;xzz|'l3ld|
|0=9c09s|klkl32|
|d0-zox|m,3,a|
|x'.za|wkl;3|
|=-0poxz|3kls|
|x-]0';a|sd;ks|
|wsd|756|
|sdw|;lksd|
|pattern|askjkas|
|xp]o]xa|lk3j2|,,
|]-p[z|lks|

【讨论】:

  • (?&lt;!pattern)部分,应该是(?&lt;!\|pattern\|)吗?
  • @user189035 感谢您的指出。会更合适。我已经相应地修改了我的答案。
  • 我确认答案有效。虽然问题中没有具体说明,但这将在大文件上运行,因此我不能保证它们适合内存。
【解决方案3】:

一个 awk 解决方案:

awk -v pa=pattern '
    $0 ~ pa {
        do {
            hold=$0;
            getline;
            hold=hold "\n" $0;
            getline;
        } while(match($0, pa));
        print hold
    }
    1' trial.txt

这个想法是缓冲与模式匹配的行,然后是后面的行。如果下一行也与模式匹配,则循环,这一次缓冲最近匹配的行和它后面的行。这具有删除需要替换的行的效果。

当循环停止时,缓冲区包含的第一行要么是替换已删除行的行,要么只是不被删除的第一个模式匹配。无论哪种方式,缓冲区的内容都会被打印出来。

需要最后的 1 语句来打印结束 while 循环的行以及在匹配模式之后不是第一或第二行的所有其他行。

【讨论】:

  • 谢谢:我有点偏见,因为我发现 awk 更具可读性,所以你的解决方案是我个人最喜欢的;)我试图在 awk 中做到这一点,但我一生都做不到把这个想出来(解决;计算出;弄明白。它比 @zdim 在 1GB 文件上的解决方案(稍微快一点)。
【解决方案4】:

更新答案:以下 sed 解决方案应该可以工作:

  sed '/\n/!N;/\n.*\n/!N;/^|pattern|.*\n.*,,\n|pattern|/!{P;D;};s/[^\n]*\n//;D;'

解释:

  • /\n/!N 如果 P 空间只有一行,请阅读下一行
  • /\n.*\n/!N 如果 P 空间只有两行,读入第三行
  • /^|pattern|.*\n.*,,\n|pattern|/ 测试第一行和第三行是否以|pattern|开头,中间行以两个逗号结尾
  • !{P;D;} 如果匹配失败,则打印第一行并重新开始
  • s/[^\n]*\n//;D; 否则,当匹配成功时,删除前两行,重新开始。

【讨论】:

  • 另外,不应该在第二个|pattern| 后面跟着.*(只是猜猜,我不是 sed 神或任何东西)。别的东西:第三个|pattern|:它应该保留模式的第三行(not |pattern|。我已经编辑了示例以使其更清晰)
  • 我在编辑中举了一个例子,代码似乎没有给出正确的答案。
  • 谢谢。只是为了确认此代码 正在 给出所需的结果。再次感谢你。有关信息,在我的系统上运行(在 1GB 文件上)所需的时间大约是 @zdim 解决方案的 3 倍。
  • @user189035:速度很有趣!无论如何,最好有两个独立的解决方案,这样您就可以将一个与另一个进行对比。
【解决方案5】:

这可能对你有用(GNU sed):

sed ':a;N;s/[^\n]*/&/3;Ta;/^|pattern|.*\n.*,,\n|pattern|/{/,,\n.*\n\|,,$/!{s/.*\n//;ba}};P;D' file

用文件的接下来的三行填充模式空间。如果第一个模式与当前三行匹配,并且第一行或第三行都不是以,, 结尾,则删除前两行并重复。否则打印并删除三行窗口的第一行并重复。

【讨论】:

  • 谢谢。我确认它通过了我的测试数据集。在我的机器上,在我用于测试的 1GB 文件上,它似乎比 @Graeme 的 awk 解决方案慢得多(迄今为止最快的解决方案)。会不会是正则表达式搜索比他使用的match() 慢?我对这些事情的了解接近 0,但我很想知道。
猜你喜欢
  • 2021-01-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-10-13
  • 2019-08-31
  • 2017-06-16
  • 2019-10-14
  • 1970-01-01
相关资源
最近更新 更多