Skip to content
Advertisement

How can I insert spaces or characters every “n” spaces while tailing a file?

I am tailing a log file and want to make it more readable.

The current output is something like this:

HH:MM:SS.ss CONTROL:00011100001110101010111000000000
HH:MM:SS.ss INDICATION:00000001110101001111010101011011

I want the output to be more like this:

HH:MM:SS.ss CONTROL:00011100 00111010 10101110 00000000
HH:MM:SS.ss INDICATION:00000001 11010100 11110101 01011011

It would be great if sed could be used to insert the spaces.

The spaces need to be every 8 characters — it will always be in binary data after the last : in octets (but the octets are missing the spaces I want to see).

Advertisement

Answer

This code works with both GNU and BSD (macOS) versions of sed:

sed -e ':a' -e 's/^(.*:([01]{8} )*)([01]{8})([^ ])/13 4/' -e 't a'

Given the data file:

HH:MM:SS.ss CONTROL:00011100001110101010111000000000
HH:MM:SS.ss INDICATION:00000001110101001111010101011011
17:49:23.96 MODIFIED:0100010010101010101101010101010101001010101010111110100010011101

it gives the output:

HH:MM:SS.ss CONTROL:00011100 00111010 10101110 00000000
HH:MM:SS.ss INDICATION:00000001 11010100 11110101 01011011
17:49:23.96 MODIFIED:01000100 10101010 10110101 01010101 01001010 10101011 11101000 10011101

The first -e command creates a label a; the third jumps to the label a if the commands in between made a substitution (it’s a loop in sed). The fun is all in the middle command:

s/^(.*:([01]{8} )*)([01]{8})([^ ])/13 4/

The (…) notation captures information that can be referred to with n in the replacement clause. They can nest, too. The {8} requires 8 (in this case) of the previous unit. The previous unit is [01], the binary digits.

Overall, it captures everything up to the last colon : plus 0 or more units of 8 binary digits followed by a blank (and captures all this as 1; there’s also a 2 in there, but I don’t use it), plus one unit of 8 binary digits (captured as 3) followed by a non-blank (captured as 4). It replaces them with 13 4.

Because the 4 needs to be part of the next sequence of 8 binary digits, you need the looping rather than a g modifier on the substitute command.

FWIW: I wrote the code in a file sed.script containing:

:a
s/^(.*:([01]{8} )*)([01]{8})([^ ])/13 4/
t a

and then ran:

sed -f sed.script data

That can sometimes be a useful technique. Here, it’s not critical, but it can simplify life, especially if you need to process quotes — single, double, back quotes — in the sed script. The file is not affected by the shell interpreting the contents of the regexes.

Advertisement