删除精确匹配前后的行 - linux

删除精确匹配前后的行 - linux

我在 /etc/test/host.conf 中有一个这样的文件:

# this is a test
host = example.com
private = 192.168.1.1
# end of test

# this is a test
host = example.com
private = 192.168.1.2 
# end of test 

我想找到“private = 192.168.1.1”行并将其删除,同时我还想删除其前两行和后一行。

我之前搜索过,但是找不到适合我的 sed 命令。

答案1

要查找并删除“private = 192.168.1.1”行以及其前两行和后一行,可以使用以下命令:

# Set the file path
file_path="/etc/test/host.conf"

# Use the "grep" command to find the line containing "private = 192.168.1.1"
line_number=`grep -n "private = 192.168.1.1" $file_path`

# Extract the line number from the output of the "grep" command
line_number=${line_number%:*}

# Use the "sed" command to delete the line containing "private = 192.168.1.1" and the two lines before and one line after it
sed -i "$((line_number-2)),$((line_number+1))d" $file_path

第一条命令使用“grep”命令在指定路径的文件中查找包含“private = 192.168.1.1”的行号。第二条命令从“grep”命令的输出中提取行号。第三条命令使用“sed”命令删除包含“private = 192.168.1.1”的行以及其前两行和后一行。

答案2

解决方案sed

sed '

: start
/\n.*\n.*\n/ b ready
N
b start

: ready
s_.*\n.*\nprivate = 192\.168\.1\.1\n.*__
/./ P
D

' /etc/test/host.conf

解释

对于 中的多行操作sed,“NPD”方案很有用。在您的例子中,我从: startstart任意标签)和b start(“转到标签”)之间的循环开始。循环将行(N)附加到模式空间,直到\n.*\n.*\n匹配。b ready只要模式空间中恰好有三个换行符(即四行;模式空间中的最后一行永远不会以换行符结尾),就会跳出循环。

: ready当模式空间中恰好有四行时,代码就会到达。s当且仅当第三行完全匹配时,Next 将用空值替换所有四行private = 192\.168\.1\.1(点是特殊的,需要对其进行转义才能与点完全匹配)。

现在模式空间要么是空的,要么仍然包含四条线。

SoleP打印模式空间直到第一个嵌入的换行符。如果没有嵌入的换行符,则P工作方式与 类似p,即打印整个模式空间和隐式换行符。现在:

  • 如果我们的模式空间是空的(四行被删除了s),我们就不想要这个隐式的换行符;
  • 但是如果模式空间不为空,我们希望将四行窗口移动一行。

/./ P是条件P/./测试模式空间是否包含至少一个字符(它甚至可能是空行之间的换行符)。P当且仅当测试成功时,才会执行该命令。如果测试失败,则此步骤中不会打印任何内容。

SoleD删除模式空间直到第一个嵌入的换行符并重新启动循环(不读取新行输入)。但如果没有嵌入的换行符,则D工作方式类似于d,即删除整个模式空间并启动新循环(读取新行输入)。现在:

  • 如果我们的模式空间是空的(四行被删除了s),那么D将开始一个新的循环,一行新的输入将被放入模式空间;
  • 但如果模式空间不为空则将D删除P刚刚打印的内容,模式空间将包含三行。

重新开始循环或开始新的循环意味着从头开始执行。无论模式空间中有一行还是三行,循环都会将行追加到模式空间,直到有四行,然后代码才会跳转到: ready。这样四行窗口就会遍历整个文件。


笔记

  • 模式空间的自动打印在这里完全按照您的预期工作。P负责在四行窗口前进时打印行;模式空间的自动打印负责在文件末尾打印剩余的行(如果有)。

  • 使用-i如果你愿意的话。代码可能像这样:

    sed -i.bak '
    ' /etc/test/host.conf
    

    我建议你先进行测试-i

  • 你写了:

    我想找到该private = 192.168.1.1行并将其删除,同时还想删除其前两行和后一行。

    我的代码是“全有或全无”,它会以四行为一包来删除行。我的意思是,如果没有“后面的行”或者“前面的行”少于“两行”,那么甚至“该private = 192.168.1.1行”也不会被删除。

  • 代码不会在删除第一个匹配的行后停止,它能够删除多包行。

  • 如果匹配的四行包重叠,则不会删除所有行。例如此代码片段:

    foo
    bar
    # this is a test
    host = example.com
    private = 192.168.1.1
    # overlapping
    host = example.edu
    private = 192.168.1.1
    # end of test
    

    将转换为:

    foo
    bar
    host = example.edu
    private = 192.168.1.1
    # end of test
    

    因为在删除任何匹配的四行包之后,算法会读取至少四行新行,然后才能删除更多内容。

答案3

尝试使用 bash 脚本和 4 行循环缓冲区(数组):

#!/bin/bash

lines=()
while IFS=$'\n' read -r line ; do
    # accumulate up to 4 lines in an array
    lines+=("$line")
    if [ ${#lines[@]} -eq 4 ] ; then
        # if the pattern is detected in the 3rd line, drop the accumulated lines
        if [ $(echo ${lines[2]} | grep "private = 192.168.1.1" | wc -l) -eq 1 ] ; then
            lines=()
        else
            # otherwise output the 1st line and frop it from the array
            echo "${lines[0]}"
            lines=("${lines[@]:1}")
        fi
    fi
done
# at the end output the possible remaining lines
while [ ${#lines[@]} -gt 0 ] ; do
    echo "${lines[0]}"
    lines=("${lines[@]:1}")
done

但是,如果行包含双引号,则不起作用"(编辑:实际上,看起来即使带引号也能起作用)

相关内容