删除匹配行之后的 X 行和之前的 Y 行

删除匹配行之后的 X 行和之前的 Y 行

我有以下字符串:

core1@pci0:2:0:1:  class=0x020000 rev=0x00 hdr=0x00 vendor=0x1453
    vendor     = 'MicSystem'
    device     = 'controller'
    class      = network
    subclass   = ethernet
none65@pci0:2:0:2:      class=0x080100 rev=0x00 hdr=0x00 vendor=0x1453
    vendor     = 'MicSystem'
    device     = 'interface'
    class      = base peripheral
    subclass   = DMA controller
core1@pci0:2:0:1:  class=0x020000 rev=0x00 hdr=0x00 vendor=0x1453
    vendor     = 'MicSystem'
    device     = 'controller'
    class      = network
    subclass   = ethernet

我必须删除“class = Base Peripheral”之前的 3 行和之后的 1 行,例如:

core1@pci0:2:0:1:  class=0x020000 rev=0x00 hdr=0x00 vendor=0x1453
    vendor     = 'MicSystem'
    device     = 'controller'
    class      = network
    subclass   = ethernet
core1@pci0:2:0:1:  class=0x020000 rev=0x00 hdr=0x00 vendor=0x1453 
    vendor     = 'MicSystem'
    device     = 'controller'
    class      = network
    subclass   = ethernet

我没能做到这一点,这是在 FreeBSD 操作系统中完成的。

答案1

实际使用埃德或者前任:

ed -s file <<EOF
g/base peripheral/-3;+4d
%p
EOF
printf '%s\n' 'g/base peripheral/-3;+4d' '%p' | ex -s file

lobalg用“基础外围”标记所有行并d删除给定范围。然后%p打印该文件。您可以使用w来保存对文件的更改。

答案2

尝试这个awk

awk '
FNR==NR && /class[[:blank:]]*=[[:blank:]]*base peripheral/{x=NR; nextfile;}
FNR!=NR && (FNR < x-3 || FNR > x+1)
' file file

送入file两次awk。第一次,找到相关行(如果有多个,则仅第一个匹配!)。第二次,打印排除要省略的行的行。

要与命令输出一起使用,请使用:

awk '...' <(command) <(command)

或者

output="$(command)"
awk '...' <(printf '%s' "$output") <(printf '%s' "$output")

答案3

最简单的方法可能如下,在每个 Unix 机器上的任何 shell 中使用任何 awk:

$ cat tst.awk
$0 ~ re {
    for (i=(NR-b); i<=(NR+a); i++) {
        skip[i]
    }
}
{ lines[NR] = $0 }
END {
    for (i=1; i<=NR; i++) {
        if ( !(i in skip) ) {
            print lines[i]
        }
    }
}

$ awk -v b=3 -v a=1 -v re='class[[:space:]]*=[[:space:]]*base peripheral' -f tst.awk file
    core1@pci0:2:0:1:  class=0x020000 rev=0x00 hdr=0x00 vendor=0x1453 device=0xa2d2 subvendor=0x1453 subdevice=0x0008
        vendor     = 'MicSystem'
        device     = 'controller'
        class      = network
        subclass   = ethernet
    core1@pci0:2:0:1:  class=0x020000 rev=0x00 hdr=0x00 vendor=0x1453 device=0xa2d2 subvendor=0x1453 subdevice=0x0008
            vendor     = 'MicSystem'
            device     = 'controller'
            class      = network
            subclass   = ethernet

如果您的输入来自管道而不是存储在 shell 中,那么只需执行whatever | awk -v ... -f tst.awk或执行其他任何操作即可将输入传递给脚本。

与上面的解决方案一样,ed将整个输入读取到内存中,但如果您的输入不是数亿行那么长,这不会成为问题。如果确实有这种情况,那么您可以实现一个滚动缓冲区,它是一个大小的数组,b这样当当前行与正则表达式不匹配时,您总是打印该数组中的第一个元素,但这需要更多的思考来编写并进行处理重叠范围更难。

答案4

尽管问题是通过删除该行之前和该行之后的这么多行来描述的,但显然这些预期编辑的效果具有更高级别的含义“删除属性class具有该值的项目base peripheral”。

有一种非常适合我们关注较大文档结构的此类转换任务的语言:TXR

我们可以使用易于其他人或 17 个月后您自己理解的代码来详细完成字段提取:

@(repeat)
@addr: class=0x@cl rev=0x@rev hdr=0x@hdr vendor=0x@ven
    vendor     = '@vendor'
    device     = '@device'
    class      = @class
    subclass   = @subclass
@  (require (nequal class "base peripheral"))
@  (output)
@addr: class=0x@cl rev=0x@rev hdr=0x@hdr vendor=0x@ven
    vendor     = '@vendor'
    device     = '@device'
    class      = @class
    subclass   = @subclass
@  (end)
@(end)

跑步:

$ txr data.txr data
core1@pci0:2:0:1: class=0x020000 rev=0x00 hdr=0x00 vendor=0x1453
    vendor     = 'MicSystem'
    device     = 'controller'
    class      = network
    subclass   = ethernet
core1@pci0:2:0:1: class=0x020000 rev=0x00 hdr=0x00 vendor=0x1453
    vendor     = 'MicSystem'
    device     = 'controller'
    class      = network
    subclass   = ethernet

在这里,模式匹配断言@(require expr)就是消除不需要的项目;当为 false 时它会失败expr

您可以使过滤变得任意复杂,并根据需要轻松地重塑输出。嵌入式 TXR Lisp 语言也具有神奇的超能力。

相关内容