【问题标题】:sed - Remove previous line and current line based on patternsed - 根据模式删除前一行和当前行
【发布时间】:2019-04-24 20:05:28
【问题描述】:

我想根据下一行的模式匹配删除上一行和当前行

这是我的示例.txt

This is test line 11
This is test line 999
This is test line 12
This is test line 13
This is test line 16
This is test line 999
This is test line 17
This is test line 18

我想匹配模式 999 并删除自身和上一行

我正在尝试这个命令,但我没有得到任何输出

sed -Ene ':a;N;/999/{d;}; ba; P' sample.txt

【问题讨论】:

  • sed 是对单个字符串执行s/old/new/ 的最佳工具。这不是你在这里所做的,所以 sed 不会是完成这项工作的最佳工具,所以当它可以做得更清晰、更简单、更健壮、更便携和/或更有效时,为什么还要使用 sed用其他工具?您应该包括一个案例,其中999 出现在您输入的第一行,并出现在 3 个连续的行中,以显示您希望如何处理这些内容,并为我们提供一些东西来充分测试潜在的解决方案。
  • 我认为这是一个很好的问题@rgd。

标签: sed


【解决方案1】:

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

sed 'N;/\n.*999/d;P;D' file

打开一个贯穿文件长度的两行运行窗口。

如果窗口的第二行包含999,则删除这两行。

否则,打印窗口的第一行,删除第一行并重复。

第 1 行或第 2 行或更多包含999 的连续行的替代解决方案:

sed -n ':a;$!N;/\n.*999/{:b;n;/999/bb;ba};/999/!P;D' file

【讨论】:

  • 如果 999 在第一行,这个将不起作用。我使用第 1 行的特殊情况解决了它。不确定是否有更好的方法。
  • @AlexHarvey OP 没有指定如果第一行包含 999 应该发生什么,也没有指定当 2 行或更多行包含 999 时应该发生什么。但是请参阅编辑。
  • 我认为第一个正则表达式不需要\n.*?
  • @AlexHarvey 我认为这只是为了在第二行读取 999。几个问题 1. 当我们从标签 B 到 A 时,要读取的下一行是我们最初离开的行还是从内部标签 B 结束的行开始?我想知道 n 何时读取包含 999 的连续行,然后当它返回标签 A 时,N 的内容将是什么
  • 它像 goto 语句一样立即无条件地分支
【解决方案2】:

在 gnu sed 上试过

 sed -Ez 's/[^\n]*\n[^\n]*999\n//g' sample.txt

【讨论】:

    【解决方案3】:

    请您尝试关注(如果可以,awk)。

    awk 'prev && $NF!=999{print prev ORS FNR,$0;prev="";next} $NF==999{prev=""} $NF!=999{prev=FNR FS $0}'  Input_file
    

    或者如果你有偶数行并且你想打印最后一个奇偶数。

    awk 'prev && $NF!=999{print prev ORS FNR,$0;prev="";next} $NF==999{prev=""} $NF!=999{prev=FNR FS $0} END{if(prev){print prev}}'  Input_file
    

    【讨论】:

    • 我只想使用 sed
    【解决方案4】:

    更全面的样本输入:

    $ cat file
    This is test line 999
    This is test line 11
    This is test line 999
    This is test line 12
    This is test line 13
    This is test line 999
    This is test line 999
    This is test line 999
    This is test line 14
    This is test line 15
    This is test line 16
    This is test line 999
    This is test line 17
    This is test line 18
    

    试试这个:

    $ cat tst.awk
    $NF == 999 {
        prev = ""
        next
    }
    {
        printf "%s", prev
        prev = $0 ORS
    }
    END {
        printf "%s", prev
    }
    
    $ awk -f tst.awk file
    This is test line 12
    This is test line 14
    This is test line 15
    This is test line 17
    This is test line 18
    

    或者如果你喜欢简洁而不是清晰:

    $ awk '$NF==999{p="";next} {printf "%s",p; p=$0 ORS} END{printf "%s",p}' file
    This is test line 999
    This is test line 11
    This is test line 999
    This is test line 12
    This is test line 13
    This is test line 999
    This is test line 999
    This is test line 999
    This is test line 14
    This is test line 15
    This is test line 16
    This is test line 999
    This is test line 17
    This is test line 18
    

    请注意,即使最后一个字段之外的其他部分包含999,或者如果最后一个字段为9999 而不是您的目标999,上述内容也将起作用,它不需要999要在脚本中多次编写/测试,如果您想测试,例如,行中的第三个字段而不是最后一个字段,您可以将 $NF 更改为 $3 =,如果您想测试整个正则表达式的行,您只需将 $NF==999 更改为 /999/,即使您的目标字符串包含正则表达式元字符,它也可以工作,并且它可以在任何 UNIX 机器上的任何 shell 中的任何 awk 中工作。

    【讨论】:

    • 它并不比我的 sed 解决方案简单得多,不是吗?我采取便携性的观点。否则,您将拥有与 sed 的 N 相同的魔法 prev = $0 ORS,而不是附加到模式空间的魔法 N
    • 我不必添加 cmets 以使我的代码清晰易懂,而且变量赋值没有什么神奇之处。此外,与 seds N 不同,如果我们想要增强以删除目标行之前的 10 行而不是 1 行,我不必将该语句写 10 次来保存 10 行,我只需制作 prev a带有循环打印的 10 行数组,如果 OP 希望能够使用在运行时指定的可变行数,这与 awk 中的解决方案与静态值的解决方案完全相同,但在 1 个 sed 脚本中是不可能的。
    • 重点是 awk 脚本有好处,而 sed 脚本没有好处。使用 awk:需要打印已删除行 stderr 的计数?琐碎的。需要测试 999 是否在特定领域?琐碎的。需要在包含正则表达式元字符时将输入字符串 (999) 测试为文字?琐碎的。还需要在目标行之后删除行吗?琐碎的。如果它们包含其他字符串,只需要删除前面的行吗?琐碎的。等等,等等。使用 awk 如果/当需求发生变化时,你通常只是建立在你已经拥有的东西上,而使用 sed 通常要么是不可能的,要么是重写
    • 哦,拜托,awk 只是 python 或 C 或任何其他基于算法的语言,具有更少的语言结构、六个关键字、一个隐式 while read split 循环和隐式 ifs 围绕 @ 987654338@ 块。另一方面,sed 在语法和语义上都大不相同。 wrt 5 行脚本的可维护性 - 您不只是以某种方式编写脚本以准备增强该脚本,而是以这种方式编写它,以便当下一个类似任务出现时,您可以基本上以相同的方式编写下一个脚本但在必要时会有细微差别。
    • 编写可维护的脚本不应该是你在必要时决定做的事情,它应该是你应该做的事情,除非有迫切的需要,例如出于性能原因。无论如何,正如我在另一个线程中所说,我花了 10 年时间使用 sed(和 shell)来操作文本,然后我才决定开始使用 awk 来处理不仅仅是 s/old/new/ 的事情,听起来你只是在以此开始你自己的旅程,并乐于继续它——一切顺利。
    【解决方案5】:

    对于 sed1 解决方案,它处理所有边缘情况(前 2 行中的 999 或 999 的连续行):

      sed '
        1{
          /999/d  # Special case needed for line 1. Delete if it contains 999.
        }
        $!N     # Append next line. $!N stops exit w/o printing at EOF.
        /999/d  # If pattern space contains 999, d & begin next cycle.
        P       # If we get to here, there is no 999. Print to first newline.
        D       # Delete to first newline.                                   
      ' FILE
    

    输出:

    This is test line 12
    This is test line 13
    This is test line 17
    This is test line 18
    

    1 在 BSD (Mac OS X) 和 GNU sed 上测试。

    【讨论】:

    • 谢谢你,如果我需要使用流控制,你能告诉我我的 sed 哪里出了问题
    • @rgd,你的脚本是:a; N; /999/d; ba; PN 将下一行附加到模式空间。然后如果/999/delete 模式空间完全并开始下一个循环。如果不是/999/,请转到a。所以你的P 命令无法访问,这就是你没有输出的原因。
    • 谢谢,我该如何修复我的代码,我的意思是我需要将P 放在代码中的哪个位置?
    猜你喜欢
    • 1970-01-01
    • 2020-01-24
    • 1970-01-01
    • 2019-11-10
    • 1970-01-01
    • 1970-01-01
    • 2017-10-13
    • 1970-01-01
    • 2020-01-27
    相关资源
    最近更新 更多