【问题标题】:How to delete 5 lines before and 6 lines after pattern match using Sed?如何使用 Sed 删除模式匹配之前的 5 行和之后的 6 行?
【发布时间】:2017-09-12 17:59:07
【问题描述】:

我想在文件中搜索模式“xxxx”并删除该模式之前的 5 行和该匹配之后的 6 行。我如何使用 Sed 做到这一点?

【问题讨论】:

    标签: linux unix sed grep


    【解决方案1】:

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

    sed ':a;N;s/\n/&/5;Ta;/xxxx/!{P;D};:b;N;s/\n/&/11;Tb;d' file
    

    保持 5 行的滚动窗口,遇到指定字符串时再添加 6 行(总共 11 行)并删除。

    注意这是一个barebones 解决方案,很可能需要根据您的特定需求进行定制。诸如:如果整个文件中有多个字符串怎么办?如果字符串在前五行之内,或者多个字符串在五行之内,等等等等。

    【讨论】:

    • 嗨@potong &/5 中的& 是什么意思,s/\n/&/11s/\n/&/5 中的每个小符号是什么意思?
    • @Minnie s/\n/&/5 表示:如果出现换行符\n,则用其自身替换第五次出现(5 是命令末尾的标志)(&是命令左侧匹配内容的简写,即正则表达式)。
    【解决方案2】:

    这是使用 awk 的一种方法。我假设您还想删除该行本身并且该文件足够小以适合内存:

    awk '{a[NR]=$0}/xxxx/{f=NR}END{for(i=1;i<=NR;++i)if(i<f-5||i>f+6)print a[i]}' file
    

    将每一行存储到数组a。当模式/xxxx/ 匹配时,保存行号。处理完整个文件后,循环遍历数组,只打印要保留的行。

    或者,您可以先使用 grep 获取行号:

    grep -n 'xxxx' file | awk -F: 'NR==FNR{f=$1}NR<f-5||NR>f+6' - file
    

    在这两种情况下,删除的行将围绕模式匹配的最后一行。

    第三种选择是使用 grep 获取行号,然后使用 sed 删除行:

    line=$(grep -nm1 'xxxx' file | cut -d: -f1)
    sed "$((line-5)),$((line+6))d" file
    

    在这种情况下,我还添加了 -m 开关,以便 grep 在找到第一个匹配项后退出。

    【讨论】:

      【解决方案3】:

      如果你知道行号(不难获得),你可以使用类似的东西:

      filename="test"
      start=`expr $curr_line - 5`
      end=`expr $curr_line + 6`
      
      sed "${start},${end}d" $filename (optionally sed -i)
      

      当然,您必须记住附加条件,例如 start 不应小于 1 并且 end 不应大于文件中的行数。

      【讨论】:

        【解决方案4】:

        另一个 - 可能更容易理解 - 解决方案是使用 grep 查找关键字和相应的行:

        grep -n 'KEYWORD' <file>
        

        然后使用sed 获取行号,如下所示:

        grep -n 'KEYWORD' <file> | sed 's/:.*//'
        

        现在您有了行号,只需像这样使用sed

        sed -i "$(LINE_START),$(LINE_END) d" <file>
        

        删除之前和/或之后的行!仅使用-i,您将覆盖&lt;file&gt;(无备份)。

        脚本示例可以是:

        #!/bin/bash
        
        KEYWORD=$1
        LINES_BEFORE=$2
        LINES_AFTER=$3
        FILE=$4
        
        LINE_NO=$(grep -n $KEYWORD $FILE | sed 's/:.*//' )
        echo "Keyword found in line: $LINE_NO"
        
        LINE_START=$(($LINE_NO-$LINES_BEFORE))
        LINE_END=$(($LINE_NO+$LINES_AFTER))
        echo "Deleting lines $LINE_START to $LINE_END!"
        
        sed -i "$LINE_START,$LINE_END d" $FILE
        

        请注意,这仅在关键字找到一次时才有效!根据您的需要调整脚本!

        【讨论】:

          最近更新 更多