0

I am trying to strip off the digits in front of a few IP ranges below:

range 1.1.1.10 1.1.1.100; 

I want to display the following output:

IP range is: 10-100 

When I try this command:

echo ' range 1.1.1.10 1.1.1.100;' | sed -rn '/^\s*range/ s/.*\.([0-9]{1,3})\s*.*\.([0-9]{1,3});/IP range is: \1-\2/p' 

I get:

IP range is: 1-100 

But when I try this command

echo ' range 1.1.1.10 1.1.1.100;' | sed -rn '/^\s*range/ s/.*\.([0-9]{1,3})\s*.*([0-9]{1,3});/IP range is: \1-\2/p' 

I get:

IP range is: 10-0 

Am I missing something? It appears to me that sed is not looking for a space immediately after the appearance of the last octet in the first address.

1
  • sed is doing exactly what you instruct it to do. Due to the greedy nature of regex the .* in .*([0-9]{1,3}); will match up to (and not including) the last digit before the ; so either introduce a literal dot like in your 1st command e.g. .*\.([0-9]{1,3}); or rethink your regex. And btw, disabling printing to explicitly print afterwards doesn't make much sense... Commented Jun 4, 2018 at 19:35

4 Answers 4

1

Your issue has to do with the greediness of .*. You can still do it in sed, but it's easier with Perl:

$ echo 'range 1.1.1.10 1.1.1.100;' | perl -ne 'printf("IP range is %d-%d\n", (/\.(\d+)[^.]/g))' IP range is 10-100 

The Perl script matches the numbers at the end of the IP addresses and returns these as two integers which are used in printf() to print the formatted output string.

The Perl regular expression

/\.(\d+)[^.]/g 

will capture each set of digits preceded by a dot, but not followed by a dot.


In sed you don't really have to do the substitution in one go:

$ echo 'range 1.1.1.10 1.1.1.100;' | sed -E -e 's/[^ ]*\.([0-9]+);/\1/' range 1.1.1.10 100 $ echo 'range 1.1.1.10 1.1.1.100;' | sed -E -e 's/[^ ]*\.([0-9]+);/\1/' -e 's/[^ ]*\.([0-9]+) /\1-/' range 10-100 $ echo 'range 1.1.1.10 1.1.1.100;' | sed -E -e 's/[^ ]*\.([0-9]+);/\1/' -e 's/[^ ]*\.([0-9]+) /\1-/' -e 's/range/IP & is/' IP range is 10-100 

The final sed script:

s/[^ ]*\.([0-9]+);/\1/; # Isolate last number in range s/[^ ]*\.([0-9]+) /\1-/; # Isolate first number in range, add dash s/range/IP & is/; # Sort out text at start 

Which, upon inspection, can be shortened down to

s/[^ ]*\.([0-9]+)[^.]/-\1/g s/range -/IP range is / 

That is

$ echo 'range 1.1.1.10 1.1.1.100;' | sed -E -e 's/[^ ]*\.([0-9]+)[^.]/-\1/g' -e 's/range -/IP range is /' IP range is 10-100 
1

With gnu sed without control of the validity of the line

sed -E 's/.*\.(\S+)\s+.*\.(\S+);.*/IP range is: \1-\2/' 
0
sed -r '/^[[:blank:]]*range[[:blank:]]+/ { s/// # delete the previous match, "range" s/;.*// s/[[:digit:]]+\.//g # remove digits followed by dot s/[[:blank:]]+/-/ s/^/IP range is: / }' <<END range 1.1.1.10 1.1.1.100; END 
IP range is: 10-100 
0
$ echo ' range 1.1.1.10 1.1.1.100;' | sed -Ene 's/^[[:blank:]]*range [0-9.]*\.([0-9]+) [0-9.]*\.([0-9]+);.*/IP range is: \1-\2/p' IP range is: 10-100 

in parts:

^[[:blank:]]* any blanks at start of line [0-9.]*\. numbers and dots, ending with a dot ([0-9]+) capture the following numbers [0-9.]*\.([0-9]+) same, again \1 and \2 put back the captured groups s///p print if the substitution matched 

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.