ddrew78 has asked for the wisdom of the Perl Monks concerning the following question:

This script will put me in my grave. I have a file that looks like this:
address-family ipv4 vrf V1234:ABCD neighbor 192.168.254.2 route-map something in neighbor 192.168.254.2 route-map something out address-family ipv4 vrf V8765:ZYXW neighbor 192.168.254.6 route-map something in neighbor 192.168.254.6 route-map something out address-family ipv4 vrf V0918:AZBY neighbor 192.168.254.10 route-map something in neighbor 192.168.254.10 route-map something out
What I need to do is decrement the last octet of each IP address by one, so it looks like this:
address-family ipv4 vrf V1234:ABCD neighbor 192.168.254.1 route-map something in neighbor 192.168.254.1 route-map something out address-family ipv4 vrf V8765:ZYXW neighbor 192.168.254.5 route-map something in neighbor 192.168.254.5 route-map something out address-family ipv4 vrf V0918:AZBY neighbor 192.168.254.9 route-map something in neighbor 192.168.254.9 route-map something out
I have tried several variations with 'sed', but can't get anywhere. Any ideas?

Replies are listed 'Best First'.
Re: decrementing numbers
by almut (Canon) on Mar 05, 2010 at 15:23 UTC
    I have tried several variations with 'sed'

    use Perl :)

    #!/usr/bin/perl -p s/(\d+\.\d+\.\d+\.)(\d+)/$1${\($2-1)}/;

    Usage:

    $ ./decrement.pl infile >outfile

    Update: alternatively, you could use the /e option, which treats the substitution part as Perl code (instead of as a double-quoted string):

    s/(\d+\.\d+\.\d+\.)(\d+)/$1.($2-1)/e; # or (just to show another variant) s/(\d+\.\d+\.\d+\.)(\d+)/"$1".($2-1)/e;
Re: decrementing numbers
by MidLifeXis (Monsignor) on Mar 05, 2010 at 18:04 UTC

    Do you need to handle underflow (when the last octet is 0)? If so, would it just wrap to 255, or would you decrement one from the previous octet?

    If you need to do the second, one way to solve this is to convert the ipv4 address into a 32 bit number, subtract one, and then convert back. There are network-address functions (I cannot remember or find them right now, grr) for doing this, but pack / unpack is also an option for this.

    It is said that "only perl can parse Perl." I don't even come close until my 3rd cup of coffee. --MidLifeXis

      There are network-address functions...
      use Socket; my $n32 = inet_aton("192.168.254.0"); print inet_ntoa(pack("N",unpack("N",$n32)-1)); # "192.168.253.255"

      Or, in the OP's context:

      s/(\d+\.\d+\.\d+\.\d+)/inet_ntoa(pack("N",unpack("N",inet_aton($1))-1) +)/e;

      But (to quote the docs): "For portability do not assume that the result of inet_aton() is 32 bits wide, in other words, that it would contain only the IPv4 address in network order."

      The last octet will never be "0". These WAN IP's, running off /30's. But thanks for considering it. I'm trying to work with some of the fixes in here, but can't get any of them to work for me. Guess I'll keep hacking away at it :-( Thanks to everyone for their help.
        ...but can't get any of them to work for me.

        If you let us in on what problems you have, we might be able to assist...   In other words, what exactly have you tried, and how did it fail?

Re: decrementing numbers
by jwkrahn (Abbot) on Mar 05, 2010 at 18:57 UTC
    use Socket; while ( <> ) { s{([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})}{ $addr = inet_aton $1 or die "Invalid IP address\n"; inet_ntoa pack 'N', unpack( 'N', $addr ) - 1; }e; print; }
Re: decrementing numbers
by Anonymous Monk on Mar 05, 2010 at 15:16 UTC
    Try writing some perl :)