Andy J
Andy J

Reputation: 1545

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.

Upvotes: 0

Views: 101

Answers (2)

Benjamin W.
Benjamin W.

Reputation: 52181

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

$ sed -r 'N;s/^([[:space:]]*hardware.*)(\n})$/\1\n\t\2/;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})$/\1\n\t\2/

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

Upvotes: 2

tink
tink

Reputation: 15214

I took the liberty to add another curly ...

sed 'N; s/hardware.*}/\t\n}/; P; D' andy
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";
}

Upvotes: 1

Related Questions