0

Can I, in few commands, search and replace multiple lines in a file?

I'm trying to replace the 3 failover blocks from dhcp_primary by the unique failover block from dhcp_secondary, within dhcp_primary.

My goal is to copy the dhcpd.conf from a primary dhcp to the secondary (more information here: http://www.madboa.com/geek/dhcp-failover/). The failover work only if the configuration are identical, except the failover block of course; as you can see is the website's example. So I want to copy this file, but keep the failover information from the secondary.

Example dhcp_primary:

// some lines above
failover peer "A" {
...
}
failover peer "B" {
...
}
failover peer "C" {
...
}
// some lines below

Example dhcp_secondary:

// some different lines above
failover peer "D" {
...
}
// some different lines below

The expected output have to be:

// some lines above
failover peer "D" {
...
}
// some lines below

I already can extract the failover blocks :

awk '/^failover/,/^}$/' dhcp_a

awk '/^failover/,/^}$/' dhcp_b

But I don't know how to continue.

Thanks in advance.

Edit: more details for my goal.

Jeelo
  • 63
  • 2
  • 9
  • I am not familiar with failover blocks. What is the purpose of the numbers `111` and `222`? Are `dhcp_a` and `dhcp_b` two separate files, or are they blocks within the same file? – Håkon Hægland Jan 15 '14 at 11:49
  • Here, the numbers 111 and 222 are standing for replacing some lines. The failover blocks one under the other without any lines between, but there are some lines above these blocks and some lines below these block. And dhcp_a and dhcp_b are to separate files. – Jeelo Jan 15 '14 at 13:21
  • Can there be nested braces within a given failover block? Like `failover peer "D" { ... subnet { ... } }` ? And is it arbitrary where in the primary file the secondary failover block ends up? (Can you have it at the beginning of the file for instance?) – Håkon Hægland Jan 15 '14 at 13:46
  • No, there isn't nested braces within a given failover block. I extract failover block using this regexp '/^failover/,/^}$/'. The failover blocks are in the middle of the files. – Jeelo Jan 15 '14 at 16:19
  • 1
    Never use ranges like `'/a/,/b/'` in awk as they make trivial solutions slightly briefer but then need a complete re-write when it gets even slightly more complicated. Just use `'/a/{f=1} f; /b/{f=0}'` instead and then you have complete control over, for example, which of the terminating lines to print, what other conditions you need to test, and you don't end up having to re-test the same condition (a common problem with the `'/a/,/b/'` approach). For example, to not print the terminating matching line - '/a/,/b/{ if (!/b/) print }' vs `'/a/{f=1} /b/{f=0} f'`. – Ed Morton Jan 15 '14 at 19:18
  • 1
    @EdMorton Yes, I too just discovered that range expression weren't very flexible.. – Håkon Hægland Jan 15 '14 at 19:22

4 Answers4

3

You can try:

awk -f a.awk dhcp_b dhcp_a

where a.awk is:

/^failover/,/^}$/{
    if (NR==FNR) {
        blk=blk $0 RS
        next
    }
    if (++i==1) {
        printf "%s",blk
    }
    next
}
NR!=FNR{  print }
Håkon Hægland
  • 39,012
  • 21
  • 81
  • 174
  • 2
    +1 This may be the first time I've ever seen `/a/,/b/` applied usefully! nit-pick - `NR!=FNR{ print }` can be abbreviated to just `NR!=FNR`. – Ed Morton Jan 15 '14 at 20:18
  • 1
    @EdMorton Thanks, I just thought I should try to use the code from the OP which used range expressions.. But I quickly found that it was not very flexible indeed.. – Håkon Hægland Jan 15 '14 at 20:23
1

This might work for you (GNU sed):

sed -n '/^failover peer/,/^}/p' dhcp_b | 
sed -e '/^failover peer/,/^}/!b;r /dev/stdin' -e 'd' dhcp_a
potong
  • 55,640
  • 6
  • 51
  • 83
  • I have some problems with this command: - b: Event not found. - sed: 1: "/^failover peer/,/^}/r ...": command r expects up to 1 address(es), found 2 I'm under freeBSD, sorry for forgetting this information. – Jeelo Jan 15 '14 at 16:09
  • @Jeelo the `/dev/stdin` is GNU specific. However I think you have not copied the solution as the `/.../!b` obviates the problem of the `r` command accepting 2 addresses. – potong Jan 15 '14 at 16:17
0

if the files are only of this structure and kind of content

NewFile=./FileName

head -1 dhcp_a > ${NewFile}
sed -n '1!{$!p;};}' dhcp_b >> ${NewFile}
tail -1 dhcp_a >> ${NewFile}

just take header and trailer of dhcp_a and block content of dhcp_b

if file is bigger (content around) use something like /failover/,/}/ as block delimiter in sed but it depend of the real content

NeronLeVelu
  • 9,908
  • 1
  • 23
  • 43
  • The content around is bigger, but I don't know how to use block delimiter in sed this way. – Jeelo Jan 15 '14 at 12:51
  • provide a sample of file (part of if it is enough) enough to estimate a delimiter that not anchor on other part of the file – NeronLeVelu Jan 15 '14 at 13:17
  • An example of file is here: http://www.madboa.com/geek/dhcp-failover/ Under "Configuring the primary" and "Configuring the secondary" My goal is to copy the dhcpd.conf from the primary to the secondary. This way, le configuration will be identical, but I have to keep the failover block. – Jeelo Jan 15 '14 at 13:28
0
$ cat tst.awk
/^failover/ { inRec  = 1 }

{
    if (NR == FNR) {
        if (inRec) {
            rec = rec $0 ORS
        }
    }
    else {
        if (inRec) {
            printf "%s", rec
            rec = ""
        }
        else {
            print
        }
    }
}

/^}/ { inRec = 0 }

$ awk -f tst.awk secondary primary
// some lines above
failover peer "D" {
...
}
// some lines below
Ed Morton
  • 188,023
  • 17
  • 78
  • 185