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