Perl/Sed 多次替换相同模式的命令

Perl/Sed command to replace same pattern multiple times

我需要使用 perl 或 sed 等命令在 /etc/xinetd.d/chargen 中设置 disable=no

/etc/xinetd.d/chargen内容为:

# description: An xinetd internal service which generate characters.  The
# xinetd internal service which continuously generates characters until the
# connection is dropped.  The characters look something like this:
# !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefg
# This is the tcp version. 
service chargen
{
        disable         = yes
        type            = INTERNAL
        id              = chargen-stream
        socket_type     = stream
        protocol        = tcp
        user            = root
        wait            = no 
}

# This is the udp version. 
service chargen
{
        disable         = yes
        type            = INTERNAL
        id              = chargen-dgram
        socket_type     = dgram
        protocol        = udp
        user            = root
        wait            = yes 
}

我用过perl命令

perl -0777 -pe 's|(service chargen[^\^]+)disable\s+=\syes|disable=no|' /etc/xinetd.d/chargen 
但它只替换了一个地方。

# description: An xinetd internal service which generate characters.  The
# xinetd internal service which continuously generates characters until the
# connection is dropped.  The characters look something like this:
# !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefg
# This is the tcp version.
service chargen
{
        disable         = yes
        type            = INTERNAL
        id              = chargen-stream
        socket_type     = stream
        protocol        = tcp
        user            = root
        wait            = no
}

# This is the udp version.
service chargen
{
        disable=no
        type            = INTERNAL
        id              = chargen-dgram
        socket_type     = dgram
        protocol        = udp
        user            = root
        wait            = yes
}

让它在两个地方都工作的正确命令是什么?

注意:我可以用 disable = no 替换 disable = yes 而无需匹配 service chargen 但我需要使用相同的 sed/perl在 /etc/xinetd.conf 中替换的命令也将有其他服务。

更新 正如乔纳森在他的评论中强调的那样,禁用可以在花括号内的任何位置。

您可以使用这个 perl 命令:

perl -0777 -pe 's/(?m)^service chargen\s*\{[^}]*disable\s*=\s*\Kyes/no/g' file

# description: An xinetd internal service which generate characters.  The
# xinetd internal service which continuously generates characters until the
# connection is dropped.  The characters look something like this:
# !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefg
# This is the tcp version.
service chargen
{
        disable         = no
        type            = INTERNAL
        id              = chargen-stream
        socket_type     = stream
        protocol        = tcp
        user            = root
        wait            = no
}

# This is the udp version.
service chargen
{
        disable         = no
        type            = INTERNAL
        id              = chargen-dgram
        socket_type     = dgram
        protocol        = udp
        user            = root
        wait            = yes
}

RegEx Demo

\K 重置报告匹配的起点。任何先前消耗的字符不再包含在最终匹配中

Awk 好吗?:

$ awk '/service chargen/,/}/{if(/disable/)sub(/yes/,"no")}1' file
...
        disable         = no
...
        disable         = no
...

解释:

$ awk '                          # well, awk
/service chargen/,/}/ {          # between service chargen {...}
    if(/disable/)                # if disable found
        sub(/yes/,"no")          # replace yes with no
}1' file                         # output

随意根据自己的喜好调整正则表达式 (/disable/)(例如 /^ *disable *=/)。

使用sed,您可以使用:

sed -e '/^service chargen/,/^}/ { /disable *= yes/ s/yes/no/; }'

第一部分搜索从 service chargen 开头的行到之后以 } 开头的第一行的行范围;在该范围内,它查找包含 disable = yesdisable= yes 之间任意数量的 space 的行,并将 yes 更改为 no.如有必要,您可以使正则表达式更复杂(没有尾随白色 space;不要编辑 service chargen2018 块,要求 } 没有尾随空白,等等)但它可能不是没必要。

您通常可以进行就地编辑,但要注意系统之间在执行此操作的语义方面的差异。 (BSD 和 macOS 需要 -i '';GNU 只需要 -i;两者都接受 -i.bak,两者的意思相同——但你有一个备份文件要清理。)