Yerry García
Yerry García

Reputation: 61

How to insert lines after second pattern with linux sed command

I want to insert this block:

host client3 {
    hardware ethernet c0:03:03:bc:30:fa;
}

after this block:

subnet 11.10.0.0 netmask 255.255.255.0 {
    range 11.10.1.2 11.10.1.254;
        group {
            filename "10M-5M-OKS2016NOV.cm";

The line: filename "10M-5M-OKS2016NOV.cm"; apears multiple times in the file. But only once inside subnet 11.10.0.0 netmask 255.255.255.0 {

Until now I can print the subnet block until the "filename":

sed -n -e :a -e '/subnet 11\.10\.0\.0 netmask 255\.255\.255\.0/,/}/{/filename "10M-5M-OKS2016NOV\.cm";/!{$!{N;ba};};p;}' dhcpd.conf

but when I try:

sed -n -e :a -e '/subnet 11\.10\.0\.0 netmask 255\.255\.255\.0/,/}/{/filename "10M-5M-OKS2016NOV\.cm";/!{$!{N;ba};};a\ \thost client3     {\n\thardware ethernet c0:03:03:bc:30:fa;\n\t}\n;}' dhcpd.conf

I get:

sed: -e expression #1, char 0: unmatched `{'

subnet 10.10.0.0 netmask 255.255.255.0 {
    range 10.10.0.2 10.10.0.254;
    group {
        filename "10M-5M-OKS2016NOV.cm";
        host client1 {
            hardware ethernet a0:b4:3d:bc:df:fa;
            }
        host client2 {
            hardware ethernet 90:6e:bb:ba:cd:d4;
            }
    }
}
subnet 11.10.0.0 netmask 255.255.255.0 {
    range 11.10.1.2 11.10.1.254;
    group {
        filename "10M-5M-OKS2016NOV.cm";
        host client1 {
            hardware ethernet c0:14:e3:bc:df:fa;
            }
        host client2 {
            hardware ethernet 90:6e:fb:ba:3d:04;
            }
    }
}
subnet 12.10.0.0 netmask 255.255.255.0 {
    range 12.10.2.2 12.10.2.254;
    group {
        filename "10M-5M-OKS2016NOV.cm";
        host client1 {
            hardware ethernet c0:a4:3d:bc:df:fa;
            }
        host client2 {
            hardware ethernet 90:6e:bb:ca:3d:04;
            }
    }
}

Upvotes: 3

Views: 167

Answers (3)

potong
potong

Reputation: 58351

This might work for you (GNU sed):

sed '/subnet 11\.10\.0\.0 netmask 255\.255\.255\.0/{:a;n;/filename "10M-5M-OKS2016NOV\.cm";/!ba;p;s/\S.*/host client3 {/p;s//    hardware ethernet c0:03:03:bc:30:fa;/p;s//}/}' file

This finds the first line containing subnet 11.10.0.0 netmask 255.255.255.0 and then reads on further until the line containing filename "10M-5M-OKS2016NOV.cm";. After printing that line it uses the line as a template to format the required detail.

Another solution, using a preformed insertion file:

cat <<\! | sed '/subnet 11\.10\.0\.0 netmask 255\.255\.255\.0/!b;:a;n;/filename "10M-5M-OKS2016NOV\.cm";/!ba;r /dev/stdin' file
        host client3 {
            hardware ethernet c0:03:03:bc:30:fa;
            }
!

Upvotes: 1

tshiono
tshiono

Reputation: 22012

Please try something like:

#!/bin/bash

# define newline and tab characters for replacement
NL=$'\n'
NL="\\$NL"
TAB=$'\t'
TAB="\\$TAB"

sed '
:l
N
$!b l
# first of all slurp all lines in the pattern space
# and perform the replacement over the lines
s/subnet 11\.10\.0\.0 netmask 255\.255\.255\.0[^}]*filename "10M-5M-OKS2016NOV\.cm";/&'"$NL$TAB"'host client3 {'"$NL$TAB$TAB"'hardware ethernet c0:03:03:bc:30:fa;'"$NL$TAB"'}/g
' dhcpd.conf

It yields the following output by using the posted lines as dhcpd.conf,

subnet 10.10.0.0 netmask 255.255.255.0 {
    range 10.10.0.2 10.10.0.254;
    group {
        filename "10M-5M-OKS2016NOV.cm";
        host client1 {
            hardware ethernet a0:b4:3d:bc:df:fa;
            }
        host client2 {
            hardware ethernet 90:6e:bb:ba:cd:d4;
            }
    }
}
subnet 11.10.0.0 netmask 255.255.255.0 {
    range 11.10.1.2 11.10.1.254;
    group {
        filename "10M-5M-OKS2016NOV.cm";
        host client3 {
                hardware ethernet c0:03:03:bc:30:fa;
        }
        host client1 {
            hardware ethernet c0:14:e3:bc:df:fa;
            }
        host client2 {
            hardware ethernet 90:6e:fb:ba:3d:04;
            }
    }
}
subnet 12.10.0.0 netmask 255.255.255.0 {
    range 12.10.2.2 12.10.2.254;
    group {
        filename "10M-5M-OKS2016NOV.cm";
        host client1 {
            hardware ethernet c0:a4:3d:bc:df:fa;
            }
        host client2 {
            hardware ethernet 90:6e:bb:ca:3d:04;
            }
    }
}
  • It initially slurps all the lines at first to process multi lines efficiently.
  • It assumes the right curly brace } does not appear in the search target block to achieve the shortest match in regex.

Hope this helps.

Upvotes: 2

Serge Ballesta
Serge Ballesta

Reputation: 148870

sed is great as a stream editor, that means to process multiple times the same actions. Here you just want to insert once a bloc of text. That would be much simpler (more readable and maintenable) with ed:

ed dhcpd.conf <<EOF
/subnet 11.10.0.0/
/filename/
a
        host client3 {
            hardware ethernet c0:03:03:bc:30:fa;
            }
.
w
q
EOF

Beware: ed is a file editor. That means that the dhcpd.conf file will be changed by the above script. Make sure to have a backup if things goes wrong...

Upvotes: 1

Related Questions