Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

I have file with IP address and netmask IP's

my target is to cut the netmask IP's from the file_with_IPs.txt and paste them to another file as file_with_only_netmask_ips.txt

remark - netmask IP can be any combination of netmask IP , and can start for example from xxx.xxx.xxx.xxx/1 until xxx.xxx.xxx.xxx/32

for example

  10.140.4.11    10.140.4.110    
  255.255.0.0    255.255.255.0    
  10.219.39.188  10.219.39.200
  10.219.39.189  10.219.39.145
  10.140.4.12    10.140.4.120
  10.219.39.138   10.219.39.140
  10.219.39.139   10.219.39.239
  255.0.0.0        255.255.0.0
  255.255.255.128    255.255.255.192

so finally I will have in file_with_IPs.txt file

  10.140.4.11    10.140.4.110    
  10.219.39.188  10.219.39.200
  10.219.39.189  10.219.39.145
  10.140.4.12    10.140.4.120
  10.219.39.138   10.219.39.140
  10.219.39.139   10.219.39.239

and in file_with_only_netmask_ips.txt I will have only the netmask IPs as the following:

  255.255.0.0    255.255.255.0
  255.0.0.0        255.255.0.0
  255.255.255.128    255.255.255.192

Please advice what the best way to separate the netmask IPs from the ordinary IP's ?

I need to write the procedure with ksh shell , and I need to run this process on Linux and Solaris machines

remark perl one linear , sed and awk can be in ksh script

share|improve this question

2 Answers 2

up vote 0 down vote accepted

One way with awk:

awk '{
    for(i=1;i<=NF;i++) {
        if($i~/^255/) {
            netmask[NR]=i>1?netmask[NR]"\t"$i:$i
        }
        else { 
            regular[NR]=i>1?regular[NR]"\t"$i:$i
        }
    }
}
END {
    for(i=1;i<=NR;i++) {
        if (regular[i]) {
            print regular[i] > "file_with_IPs.txt"
        }
        if (netmask[i]) {
            print netmask[i] > "file_with_only_netmask_ips.txt"
        }
    }
}' file

Test:

$ ls
file

$ cat file
10.140.4.11    10.140.4.110    
255.255.0.0    255.255.255.0    
10.219.39.188  10.219.39.200
10.219.39.189  10.219.39.145
10.140.4.12    10.140.4.120
10.219.39.138   10.219.39.140
10.219.39.139   10.219.39.239
255.0.0.0        255.255.0.0
255.255.255.128    255.255.255.192

$ awk '{
>     for(i=1;i<=NF;i++) {
>         if($i~/^255/) {
>             netmask[NR]=i>1?netmask[NR]"\t"$i:$i
>         }
>         else { 
>             regular[NR]=i>1?regular[NR]"\t"$i:$i
>         }
>     }
> }
> END {
>     for(i=1;i<=NR;i++) {
>         if (regular[i]) {
>             print regular[i] > "file_with_IPs.txt"
>         }
>         if (netmask[i]) {
>             print netmask[i] > "file_with_only_netmask_ips.txt"
>         }
>     }
> }' file

$ ls
file  file_with_IPs.txt  file_with_only_netmask_ips.txt

$ cat file_with_IPs.txt 
10.140.4.11     10.140.4.110
10.219.39.188   10.219.39.200
10.219.39.189   10.219.39.145
10.140.4.12     10.140.4.120
10.219.39.138   10.219.39.140
10.219.39.139   10.219.39.239

$ cat file_with_only_netmask_ips.txt 
255.255.0.0     255.255.255.0
255.0.0.0       255.255.0.0
255.255.255.128 255.255.255.192
share|improve this answer
    
one more question - for example I want to write to file as $file_with_IPs in place of file_with_IPs.txt" , ( file_with_IPs=/tmp/IPs.txt ) , when I set in awk the - > $file_with_IPs , then awk not know the path /tmp/IPs.txt , what I need to fix here ?? –  yael Jun 20 '13 at 13:25
    
You can set a variable in BEGIN statement. Check this answer –  jaypal Jun 20 '13 at 13:33

By "netmask IPs", you seem to mean things with all the set bits grouped together on the left. There are only 32 of these, from /0 through /32. You could just list them, in a grep statement (or, better, in a file you pass to grep -f).

This approach is tedious, but easy;

^0\.0\.0\.0$
^128\.0\.0\.0$
^192\.0\.0\.0$
^224\.0\.0\.0$
⋮

Since its actually a binary pattern we're looking for, you could convert it back to a number, then do some bit-twiddling on it to confirm it matches the pattern. That'd be an actual programming task, though, not something you'd throw together with grep.

Though you can do something close with bc. Here is how to test $ip:

(
    echo 'obase=2';
    echo "$ip" | sed -e 's/\([0-9]\+\)\.\([0-9]\+\)\.\([0-9]\+\)\.\([0-9]\+\)/\1*(256^3)+\2*(256^2)+\3*256+\4/'
) bc | grep -q '^1*0*$' && echo yes || echo no

If you break it down, the echo tells bc to output in binary. Then that sed line turns an IP address into an expression, to compute its numeric value. 255.255.255.0 becomes 255*(256^3)+255*(256^2)+255*256+0. So bc sees:

obase=2
255*(256^3)+255*(256^2)+255*256+0

It prints out 11111111111111111111111100000000, which is then checked to match the pattern ^1*0*$ with grep.

If you have a utility to convert an IP to a number, that'll remove most of the complexity above. Will still probably be slower than the grep with a list of patterns, though.

share|improve this answer
    
I am not sure if sed syntax also fit for solaris ? –  yael Jun 18 '13 at 20:26
    
I'm not sure, you'd have to test it (I don't have any Solaris machines to test on). But the grep with list of patterns will, that's specified by POSIX. –  derobert Jun 18 '13 at 20:31

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.