【问题标题】:Using sed to delete all lines between two matching patterns使用 sed 删除两个匹配模式之间的所有行
【发布时间】:2023-06-13 03:50:01
【问题描述】:

我有一个类似的文件:

# ID 1
blah blah
blah blah
$ description 1
blah blah
# ID 2
blah
$ description 2
blah blah
blah blah

如何使用 sed 命令删除 #$ 行之间的所有行?所以结果会变成:

# ID 1
$ description 1
blah blah
# ID 2
$ description 2
blah blah
blah blah

能否请您也解释一下?

【问题讨论】:

    标签: regex sed


    【解决方案1】:

    使用这个 sed 命令来实现:

    sed '/^#/,/^\$/{/^#/!{/^\$/!d}}' file.txt
    

    Mac 用户(防止extra characters at the end of d command 错误)需要在右括号前添加分号

    sed '/^#/,/^\$/{/^#/!{/^\$/!d;};}' file.txt
    

    输出

    # ID 1
    $ description 1
    blah blah
    # ID 2
    $ description 2
    blah blah
    blah blah
    

    说明:

    • /^#/,/^\$/ 将匹配以# 开头的行与以$ 开头的行之间的所有文本。 ^ 用于行首字符。 $ 是一个特殊字符,需要转义。
    • /^#/! 表示如果行首不是 #,则执行以下操作
    • /^$/! 表示如果行首不是 $,则执行以下操作
    • d 表示删除

    所以总的来说,它首先匹配从^#^\$ 的所有行,然后从那些匹配的行中找到不匹配 ^#不匹配的行 ^\$ 并使用 d 删除它们。

    【讨论】:

    • Mac 用户:为防止出现extra characters at the end of d command 错误,您需要在右括号sed '/^#/,/^\$/{/^#/!{/^\$/!d;};}' file.txt 前添加分号
    • 这是投反对票的理由吗?如果您喜欢答案,请点赞。 Downvote 通常用于一些不能解决 OP 问题的答案,并且由于 OP 已经接受了这个答案,这意味着它适用于 OP。不是吗?
    • 如果您想包含 # 和 $ 行以进行删除,您会怎么做?如果你想在一行的末尾找到 $,你可以这样做 $\$,
    • 然后使用:sed '/^#/,/^\$/d' file
    • 我使用 sed '/^====/,/^>>>>/d' file-with-git-merge-conflicts.xml > file-ok.xml 删除 '=======' 和 '>>>>> 分支名称之间的 git merge 冲突行,并使用 sed '/^<<<</d' 删除 '
    【解决方案2】:
    $ cat test
    1
    start
    2
    end
    3
    $ sed -n '1,/start/p;/end/,$p' test
    1
    start
    end
    3
    $ sed '/start/,/end/d' test
    1
    3
    

    【讨论】:

    • 处理 300mb 文件的速度令人印象深刻。我说的是 SSD 上的瞬间。
    • 我有点困惑,因为我不熟悉 sed 语法。不清楚第一个和第二个 sed 命令没有依赖关系 - 即两者之间的区别在于您是否要保留匹配令牌。在我测试它之前,我假设第一个命令删除了标记之间的所有内容,第二个命令删除了标记本身。如果你想去除标记之间的一个块,你只需要使用第二个命令。
    • 不知道为什么,但'1,/start/p;/end/,$p' 完全搞砸了我的工作流程,因为我依赖于这项工作。它对我根本不起作用。
    • github.com/theAkito/akito-libbash/blob/… 是有问题的行。我错过了什么?因为在我看来,这与您在答案@Lri 中所显示的完全一样。
    • 实际可行的解决方案如下:sed '/PATTERN-1/,/PATTERN-2/{//!d}' input.txt
    【解决方案3】:

    在一般形式中,如果您有一个内容格式为 abcde 的文件,其中节 a 在模式 b 之前,那么节 c 在模式 d 之前,然后在 e 部分之后,并且应用以下sed 命令,您会得到以下结果。

    在此演示中,输出由=> abcde 表示,其中字母表示输出中的部分。因此,ae 仅显示部分 ae 的输出,ace 将是部分 ac em>、e

    请注意,如果 bd 出现在输出中,则这些是出现的模式(即,它们被视为输出中的部分)。

    也不要将/d/ 模式与命令d 混淆。在这些演示中,命令始终位于最后。该模式始终位于// 之间。

    • sed -n -e '/b/,/d/!p' abcde => ae
    • sed -n -e '/b/,/d/p' abcde => bcd
    • sed -n -e '/b/,/d/{//!p}' abcde => c
    • sed -n -e '/b/,/d/{//p}' abcde => bd
    • sed -e '/b/,/d/!d' abcde => bcd
    • sed -e '/b/,/d/d' abcde => ae
    • sed -e '/b/,/d/{//!d}' abcde => abde
    • sed -e '/b/,/d/{//d}' abcde => 王牌

    【讨论】:

      【解决方案4】:

      使用 sed 的另一种方法:

      sed '/^#/,/^\$/{//!d;};' file
      
      • /^#/,/^\$/:从以# 开始的行到以$ 开始的下一行
      • //!d: 删除除与地址模式匹配的行之外的所有行

      【讨论】:

      • 你如何做到这一点,包括模式?
      • 试试这个:sed '/^#/,/^\$/d;' file.
      【解决方案5】:

      很久以前我做过类似的事情,它是这样的:

      sed -n -e "1,/# ID 1/ p" -e "/\$ description 1/,$ p"
      

      类似于:

      • -n抑制所有输出
      • -e "1,/# ID 1/ p" 从第一行开始执行,直到你的模式和 p(打印)
      • -e "/\$ description 1/,$ p" 从第二个模式执行到结束和 p(打印)。

      我可能对字符串上的一些转义有误,所以请仔细检查。

      【讨论】:

        【解决方案6】:

        以下示例删除了 "if""end if" 之间的行。

        扫描所有文件,并删除两个匹配模式之间的行(包括它们)。

        IFS='
        '
        PATTERN_1="^if"
        PATTERN_2="end if"
        
        # Search for the 1st pattern in all files under the current directory.
        GREP_RESULTS=(`grep -nRi "$PATTERN_1" .`)
        
        # Go through each result
        for line in "${GREP_RESULTS[@]}"; do
        
           # Save the file and line number where the match was found.
           FILE=${line%%:*}
           START_LINE=`echo "$line" | cut -f2 -d:`
        
           # Search on the same file for a match of the 2nd pattern. The search 
           # starts from the line where the 1st pattern was matched.
           GREP_RESULT=(`tail -n +${START_LINE} $FILE | grep -in "$PATTERN_2" | head -n1`)
           END_LINE="$(( $START_LINE + `echo "$GREP_RESULT" | cut -f1 -d:` - 1 ))"
        
           # Remove lines between first and second match from file
           sed -e "${START_LINE},${END_LINE}d;" $FILE > $FILE
        
        done
        

        【讨论】:

          最近更新 更多