Skip to content
Advertisement

Match two lines, replace with three lines

I need to use sed to replace every matching two-line pattern with a three line pattern. Here’s my input file (tempfile.txt).

lease 192.168.6.100 {
  binding state free;
  hardware ethernet 00:e0:4c:68:00:96;
}
lease 192.168.6.100 {
  binding state active;
  hardware ethernet 00:e0:4c:68:00:96;
  client-hostname "andrew-H81M-S2PH";
}
lease 192.168.6.100 {
  binding state free;
  hardware ethernet 00:e0:4c:68:00:96;
}
lease 192.168.6.100 {
  binding state active;
  hardware ethernet 00:e0:4c:68:00:96;
  client-hostname "andrew-H81M-S2PH";
}

Basically, if a client-hostname "HOSTNAME"; is missing, then it should be replaced with a tab and then a newline.

My attempt: sed 'N; /hardware.*}/d; P; D' tempfile.txt

The result is:

lease 192.168.6.100 {
  binding state free;
lease 192.168.6.100 {
  binding state active;
  hardware ethernet 00:e0:4c:68:00:96;
  client-hostname "andrew-H81M-S2PH";
}
lease 192.168.6.100 {
  binding state free;
lease 192.168.6.100 {
  binding state active;
  hardware ethernet 00:e0:4c:68:00:96;
  client-hostname "andrew-H81M-S2PH";
}

This is my desired output.

lease 192.168.6.100 {
  binding state free;
  hardware ethernet 00:e0:4c:68:00:96;
  <tab>
}
lease 192.168.6.100 {
  binding state active;
  hardware ethernet 00:e0:4c:68:00:96;
  client-hostname "andrew-H81M-S2PH";
}
lease 192.168.6.100 {
  binding state free;
  hardware ethernet 00:e0:4c:68:00:96;
  <tab>
}
lease 192.168.6.100 {
  binding state active;
  hardware ethernet 00:e0:4c:68:00:96;
  client-hostname "andrew-H81M-S2PH";
}

So as you can see, there are consistently three lines between the curlies. That’s what I’m aiming for.

Advertisement

Answer

This does the trick (piping to cat -A to show non-printable characters):

$ sed -r 'N;s/^([[:space:]]*hardware.*)(n})$/1nt2/;t;P;D' infile | cat -A
lease 192.168.6.100 {$
  binding state free;$
  hardware ethernet 00:e0:4c:68:00:96;$
^I$
}$
lease 192.168.6.100 {$
  binding state active;$
  hardware ethernet 00:e0:4c:68:00:96;$
  client-hostname "andrew-H81M-S2PH";$
}$
lease 192.168.6.100 {$
  binding state free;$
  hardware ethernet 00:e0:4c:68:00:96;$
^I$
}$
lease 192.168.6.100 {$
  binding state active;$
  hardware ethernet 00:e0:4c:68:00:96;$
  client-hostname "andrew-H81M-S2PH";$
}$

Instead of deleting matches, this captures the two lines supposed to surround the empty line and replaces with the newline and tab between. I’ve also added a few anchors for safer matching.

There is a bit of trickery involved because the pattern space contains two newlines after a substitution, but P;D prints only the first line and the starts a new cycle, which leads to unwanted newlines also after lines containing client-hostname.

Explained in more detail:

N       # Append next line to pattern space

# If the hostname is missing, insert a newline and a tab
s/^([[:space:]]*hardware.*)(n})$/1nt2/

t      # If we did the substitution, jump to end (prints complete pattern space)
P      # Print first line in pattern space
D      # Delete first line in pattern space - starts new cycle
User contributions licensed under: CC BY-SA
8 People found this is helpful
Advertisement