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

This should be easy but for the life of me I can't get split to work the way I expect / want it to. Here is the deal, this should ping an address 4 times and then strip out the packet loss and avg time. Here is the code:
#!/usr/bin/perl #tnks to Willy @ http://www.willy.com/Scripps/mrtg-data.html for most +all of this $ping_str = `/bin/ping -c4 dslreports.com`; # keep only the packets lost $loss = (split(/\,/,$ping_str))[2]; # now $loss looks like " 0%"; # keep only the avg time $avg = (split(/\//,$ping_str))[4]; # now $avg looks like "114.337"; # since we are looking for a positive integer we now round $avg = int($avg); # now return the values along with 2 zero lines. print "$loss\n"; print "$avg\n"; print "0\n"; print "0\n";
The avg time returns just fine (except I do not actually know how to round, but that is not such a big deal :-) ). My problem comes in grabbing the number of lost packets. I get too much data. Perhaps there is a better way to do this anyway. Please toss some knowledge out of the monastery windows to a peasant below. :-) Thanks James

Replies are listed 'Best First'.
Re: MRTG script should be easy
by blokhead (Monsignor) on May 20, 2003 at 07:43 UTC
    If you know the exact context of the data you want (which is a pretty reasonable assumption for ping's summary info), you can use a m// match to extract the info you want. In my opinion, using split is the wrong way to do this.
    #!/usr/bin/perl my $host = 'dslreports.com'; my $ping_data = `ping -c4 $host`; my ($sent, $rev, $lost, $min, $max, $avg); if ( $ping_data =~ /(\d+) packets transmitted, (\d+) packets received, (\d+)%/ + ) { ($sent, $recv, $lost) = ($1, $2, $3); print "\$sent=$sent, \$recv=$recv, \$lost=$lost\n"; } if ( $ping_data =~ m{round-trip min/avg/max = ([0-9\.]+)/([0-9\.]+)/([0-9\.]+) +} ) { ($min, $avg, $max) = ($1, $2, $3); print "\$min=$min, \$avg=$avg, \$max=$max\n"; } ## do what you will with those variables now...
    On my system, this outputs:
    $sent=4, $recv=4, $lost=0 $min=47.0, $avg=47.6, $max=48.4
    You should probably read this section of perlop pertaining to the m// operator.

    PS: I admit those long regexes could be broken up, simplified, /x'ed, etc, but hopefully you get the point! Update: no more wrapping within the <code>, at least under my settings.

    blokhead

      I'd have to disagree with using m// to grab data out of a system ping.

      Using a regex to grab out the data would restrict the script to running on one OS and one version of ping - as has been pointed out already, ping outputs slightly different data between versions (from a "Host is Alive" on Solaris to timings, averages and losses on my linux system at home). Changes to either the OS or the version of ping could, and probably would, break the regex, and the script would fail.

      Having said that, though, splitting as the OP is currently doing would restrict to an OS/version in a similar way, so I guess nothing would be lost by using a regexp from his PoV.

      -- Foxcub
      #include www.liquidfusion.org.uk

Re: MRTG script should be easy
by mirod (Canon) on May 20, 2003 at 10:41 UTC

    Let's see.... could a module do the trick? A search on ping on search.cpan.org returns... Net::Ping! This should both give you a convenient way to get the data in Perl, without having to resort to parsing the output of an external program, and solve portability problems if/when you want to use this tool in a different environment.

    After installing Net::Ping, a quick glance at the docs, installing Time::Hires, here is a (tested) piece of code that should work:

    #!/usr/bin/perl -w use strict; use Net::Ping; my $NB_PING=5; my $host="dslreports.com"; my( $ok, $nok, $total_time)=(0,0,0); # use the icmp protocol to emulate the unix ping # note that this implies that the script should be # run as root (or suid root) my $p = Net::Ping->new( "icmp"); $p->hires(); # needs Time::Hires foreach (1..$NB_PING) { my ($ret, $duration, $ip) = $p->ping($host, 3); # 3 is the time ou +t in sec if( $ret) { $ok++; $total_time+= $duration * 1000; } else { $nok++; } } $p->close(); # we don't want division by 0 errors do we? my $average= $ok ? int($total_time / $ok) : 0; my $lost= int ( ($nok / ( $ok + $nok)) * 100); print "$lost%\n$average\n0\n0\n";

      running MRTG as root would be lame. some systems don't allow suid scripts. somewhere along the line you'll need an external progam (to suid root) to run the ping, might as well be ping.

      if you're monitoring a lot of hosts, check if your router (or another of your SNMP agents) supports RFC 2925 Ping MIB. then you can request that the agent ping for you (and even send traps on failure) and collect the results from regular SNMP queries.

Re: MRTG script should be easy
by Tanalis (Curate) on May 20, 2003 at 07:45 UTC
    Hi,

    As Skeeve points out, ping returns slightly differently on every system, so some output would help immensely.

    It's maybe worth trying swapping the /\,/ for "," - this shouldn't make any difference (in my opinion, at least), but it seems to on my system (Perl 5.004.04 on Solaris).

    An alternative way to do this, rather than the way you're doing it currently, would be to make use of the Net::Ping module. This has the capability of returning the ping time for individual pings in milliseconds, and from that it should be trivial to ping a host a number of times and calculate your average time and packet loss.

    As far as rounding goes, you can use the printf and sprintf functions to output formatted numbers - it'll round into the bargain. For example ..

    printf "%.1f", 29.28; # outputs 29.3

    I hope that helps a little - good luck with the split ...

    -- Foxcub
    #include www.liquidfusion.org.uk

Re: MRTG script should be easy
by Skeeve (Parson) on May 20, 2003 at 07:33 UTC
    It would be much easier to help you if you give us an example of the output of your ping. Mine for example has no "," in it. So I assume mine looks different from yours. The output of ping on Solaris is even worse as it is just: "Machine is alive" ;-)

      Whenever I write code that tries to parse some complex string, I put a comment just above the code which includes a literal representative sample. This is according to the principle, "strategy in comments, tactics in code."

      --
      [ e d @ h a l l e y . c c ]

      Thank you all very much, for your kind assistance. I lost my camel book and, I guess, my brain with it. :-) Here is the output of my ping:
      PING dslreports.com (209.123.109.175) from 10.0.1.1 : 56(84) bytes of +data. 64 bytes from www.dslreports.com (209.123.109.175): icmp_seq=0 ttl=51 +time=89.483 msec 64 bytes from www.dslreports.com (209.123.109.175): icmp_seq=1 ttl=51 +time=119.926 msec 64 bytes from www.dslreports.com (209.123.109.175): icmp_seq=2 ttl=51 +time=89.899 msec 64 bytes from www.dslreports.com (209.123.109.175): icmp_seq=3 ttl=51 +time=99.920 msec --- dslreports.com ping statistics --- 4 packets transmitted, 4 packets received, 0% packet loss round-trip min/avg/max/mdev = 89.483/99.807/119.926/12.344 ms
      and here is the output of my script (obviously the numbers are not exactly the same since I did them one after the other):
      0% packet loss round-trip min/avg/max/mdev = 89.908/91.193/95.024/2.221 ms 91 0 0
      Having said that, I think I may look at the net:ping option in general and the graciously supplied script in particular. I briefly thought about NET:PING last night but this script was so close to working as it was, I hated to start from scratch and rewrite it. Especially if it turned out, I was just missing something small. Thank you all again, if any of you ever run for something...you have my vote :-) James
        Okay. So here is how I would do it:
        use strict; my (%result); # Example lines searched for # 4 packets transmitted, 4 packets received, 0% packet loss # round-trip min/avg/max/mdev = 89.483/99.807/119.926/12.344 ms while (<DATA>) { SWITCH: { /transmitted/ && do { # This will "eat up" all "packets"-information $result{$2}=$1 while /(\d+)\s+packets\s+(\S+)/ +g; last SWITCH; }; /round-trip/ && do { # this will find all values seperated by / # and will store them under their key /(\S+)\s*=\s*(\S+)/; @result{split m(/),$1}= split m(/),$2; last SWITCH; }; } } # Your %result-keys depend on the output of ping print $result{'transmitted'}-$result{'received'}," packet(s) lost\n"; print $result{'avg'}," average\n"; print Dumper(\%result); __END__ PING dslreports.com (209.123.109.175) from 10.0.1.1 : 56(84) bytes of +data. 64 bytes from www.dslreports.com (209.123.109.175): icmp_seq=0 ttl=51 +time=89.483 msec 64 bytes from www.dslreports.com (209.123.109.175): icmp_seq=1 ttl=51 +time=119.926 msec 64 bytes from www.dslreports.com (209.123.109.175): icmp_seq=2 ttl=51 +time=89.899 msec 64 bytes from www.dslreports.com (209.123.109.175): icmp_seq=3 ttl=51 +time=99.920 msec --- dslreports.com ping statistics --- 4 packets transmitted, 4 packets received, 0% packet loss round-trip min/avg/max/mdev = 89.483/99.807/119.926/12.344 ms
        Example output:
        0 packet(s) lost 99.807 average $VAR1 = { 'received,' => '4', 'avg' => '99.807', 'min' => '89.483', 'mdev' => '12.344', 'max' => '119.926', 'transmitted,' => '4' };
Re: MRTG script should be easy
by NetWallah (Canon) on May 21, 2003 at 00:01 UTC
      Thank You, Thank You, Thank You! I would have written back sooner but I have been too busy tweeking my working MRTG system :-) When I was first looking for info on MRTG, I found a person who said hat you do not have to use SNMP and explained how to use your own scripts. That set me off on a tangent making that work. I never even thought to look for prebuilt scripts. Thank you for setting me straight. MRTG-PING-PROBE ROCKS...exactly what I was looking for. Thanks again; James