简体   繁体   中英

Perl/Sed command to replace same pattern multiple times

I need to set disable=no in /etc/xinetd.d/chargen using commands like perl or sed.

/etc/xinetd.d/chargen content is:

# 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 
}

I have used perl command

perl -0777 -pe 's|(service chargen[^\^]+)disable\s+=\syes|\1disable=no|' /etc/xinetd.d/chargen
but it is replacing at only one place.

 # 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 } 

what is the proper command to make it work in both places?

NOTE : I could have replaced disable = yes with disable = no without matching service chargen but I need to use same sed/perl command to replace in /etc/xinetd.conf which will have other services too.

UPDATE As Jonathan highlighted in his comment, disable can be at any position inside the flower bracket.

Using sed , you can use:

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

The first part searches for ranges of lines from one starting service chargen to the first line afterwards that starts with } ; within that range, it looks for lines containing disable = yes with arbitrary numbers of spaces between disable and the = yes , and changes the yes to no . If necessary, you can make the regexes fussier (no trailing white space; don't edit service chargen2018 blocks, demand the } have no trailing blanks, etc.) but it probably isn't necessary.

You can often do in-place editing, but beware of differences between systems in the semantics of how you do that. (BSD and macOS require -i '' ; GNU only requires -i ; both accept -i.bak and it means the same in both — but you have a backup file to cleanup.)

You may use this perl command:

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 resets the starting point of the reported match. Any previously consumed characters are no longer included in the final match

Awk ok?:

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

Explained:

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

Feel free to tune the regex ( /disable/ ) to your liking (for example /^ *disable *=/ ).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM