Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Formatting IP with sed

Tags:

bash

sed

ip

I am trying to figure out how to do the following with sed:

I got a list of IPv4 addresses and I am trying to make them all uniform in the display. So for example: 1.2.4.32 would be 001.002.004.032. 10.125.62.1 would be 010.125.062.001.

I am trying to use sed to do it because that's what I am learning right now.

I got these two, which will take any one or two digit number and append zeros at the front.

sed 's/\<[0-9][0-9]\>/0&/g' file
sed 's/\<[0-9]\>/00&/g' file

But that runs into a more practical problem in that my input file will have single or double digits numbers in other non-IP address places. Example:

host-1 1.2.3.32

So I need a way for it to look for the full IP address, which I thought could be achieved by this

sed 's/\.\<[0-9]\>/00&/g'

but not only does that ignore the case of 1.something.something.something, but also it appends the 00 at the end of 3rd octet for some reason.

echo "10.10.88.5" | sed 's/\.\<[0-9]\>/00&/g'
10.10.8800.5

Sample file:

Jumpstart Server jumo     10.20.5.126
Jumpstart Server acob     10.20.5.168
NW1 H17  Node cluster     10.10.161.87
NW1 H17  Node-1       10.10.161.8
NW1 H17  Node-2       10.10.161.9
ts-nw1      10.10.8.6
like image 953
D.Zou Avatar asked May 14 '16 05:05

D.Zou


1 Answers

The idiomatic way of changing only parts of a line is to copy it to the hold space, remove the parts we're not interested in from the pattern space, get the hold space back and then rearrange the pattern space to replace the part we've changed with our new version.

This should work (replace -r with -E for BSD sed):

sed -r 'h                  # Copy pattern space to hold space

# Remove everything except IP address from pattern space
s/.*\b(([0-9]{1,3}\.){3}[0-9]{1,3})\b.*/\1/

s/([0-9])+/00&/g           # Prepend '00' to each group of digits
s/[0-9]*([0-9]{3})/\1/g    # Only retain last three digits of each group
G                          # Append hold space to pattern space

# Replace old IP with new IP
s/(.*)\n(.*)\b([0-9]{1,3}\.){3}[0-9]{1,3}\b(.*)/\2\1\4/' infile

The last step is the most complicated one. Just before it, a line looks like this (newline as \n, end of line as $):

010.020.005.126\nJumpstart Server jumo     10.20.5.126$

i.e., our new and improved IP address, a newline, then the complete old line. We now capture the underlined groups:

010.020.005.126\nJumpstart Server jumo     10.20.5.126$
^^^^^^^^^^^^^^^  ^^^^^^^^^^^^^^^^^^^^^^^^^^           ^
      (.*)     \n          (.*)              \b...\b  (.*)
       \1                   \2                  \3     \4

and rearrange the line by using group 2, then groups 1 (our new IP) and 4. Notice that

  • There are four capture groups, but the third one is just there to help describe an IP address, we don't actually want to retain it, hence \2\1\4 in the substitution (there are no non-capturing groups in sed).
  • The last capturing group (after the IP address) is empty, but having it makes it possible to use this for lines that have the IP address anywhere.
  • This only replaces the first IP address on each line, in case there are several.

The overall output is

Jumpstart Server jumo     010.020.005.126
Jumpstart Server acob     010.020.005.168
NW1 H17  Node cluster     010.010.161.087
NW1 H17  Node-1       010.010.161.008
NW1 H17  Node-2       010.010.161.009
ts-nw1      010.010.008.006

The same as a solidly unreadable one-liner:

sed -r 'h;s/.*\b(([0-9]{1,3}\.){3}[0-9]{1,3})\b.*/\1/;s/([0-9])+/00&/g;s/[0-9]*([0-9]{3})/\1/g;G;s/(.*)\n(.*)\b([0-9]{1,3}\.){3}[0-9]{1,3}\b(.*)/\2\1\4/' infile

\b is a GNU extension. The script mostly works without it as well; using it makes sure that blah1.2.3.4blah is left alone.

like image 97
Benjamin W. Avatar answered Oct 06 '22 22:10

Benjamin W.