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

Dear monks. I have a large ping result output like following:

[root@smokeping ping_analisis]# ping -c3 8.8.8.8; ping -c3 8.8.4.4 PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data. 64 bytes from 8.8.8.8: icmp_seq=1 ttl=117 time=5.92 ms 64 bytes from 8.8.8.8: icmp_seq=2 ttl=117 time=6.76 ms 64 bytes from 8.8.8.8: icmp_seq=3 ttl=117 time=5.87 ms --- 8.8.8.8 ping statistics --- 3 packets transmitted, 3 received, 0% packet loss, time 2003ms rtt min/avg/max/mdev = 5.873/6.184/6.760/0.417 ms PING 8.8.4.4 (8.8.4.4) 56(84) bytes of data. 64 bytes from 8.8.4.4: icmp_seq=1 ttl=116 time=8.56 ms 64 bytes from 8.8.4.4: icmp_seq=2 ttl=116 time=8.46 ms 64 bytes from 8.8.4.4: icmp_seq=3 ttl=116 time=8.71 ms --- 8.8.4.4 ping statistics --- 3 packets transmitted, 3 received, 0% packet loss, time 2006ms rtt min/avg/max/mdev = 8.460/8.578/8.715/0.129 ms

I need to map every host I ping with each result.

This is my code

#!/usr/bin/perl @hosts=("8.8.8.8","8.8.4.4"); foreach(@hosts){ $command .= "ping -c 3 $_; "; } my @lines = qx/$command/; foreach(@lines){ if(/PING/){ foreach $host(@hosts){ if(/PING ($host).*/){ $host_key = $host; } } }elsif(/rtt min\/avg\/max\/mdev = (.*)\/(.*)\/(.*)\/(. +*) .*/){ print "$host_key min:$1 avg:$2 max:$3 mdev:$4\ +n"; } }

Even my code is working , I would like to share it since this is a common task and there is probably a better way to do it.

Hope to hear some feedback.

Thanks again,

Leo.

Replies are listed 'Best First'.
Re: Create a map from multiple ping output
by NetWallah (Canon) on Sep 21, 2020 at 00:25 UTC
    Here is a "simpler" version of your code:
    #!/usr/bin/perl use strict; use warnings; my @hosts=("8.8.8.8","8.8.4.4"); for my $h (@hosts){ my $command = "ping -c 3 $h"; for(qx/$command/){ next unless m{rtt min/avg/max/mdev = "}; my @v= m{([\d\.]+)[/\s]}g; print "$h min:$v[0] avg:$v[1] max:$v[2] mdev:$v[3]\n"; last; } $? and print "ERROR:Ping to $h failed err:" , $?>>8,"\n"; }
    Untested.

                    "Imaginary friends are a sign of a mental disorder if they cause distress, including antisocial behavior. Religion frequently meets that description"

Re: Create a map from multiple ping output
by kcott (Archbishop) on Sep 21, 2020 at 12:12 UTC

    G'day Leo,

    "... there is probably a better way to do it."

    There would be many ways to this. The unqualified term "better" is highly subjective: if it relates to efficiency, then Benchmark possible solutions; if it relates to other criteria, you'll need to tell us about that or assess for yourself.

    Here's one way you could do it:

    $ perl -e ' use strict; use warnings; use autodie ":all"; my @hosts = qw{8.8.8.8 8.8.4.4}; my $command = join ";", map "ping -c3 ".$_, @hosts; my $result = qx{$command}; my $re = qr{(?s:^(\S+).+?(\S+)\s+ms\s*$)}; my $fmt = "%s min:%s avg:%s max:%s mdev:%s\n"; open my $mh, "<", \$result; { local $/ = "PING "; while (<$mh>) { chomp; next unless /$re/; printf $fmt, $1, split /\//, $2; } } ' 8.8.8.8 min:16.929 avg:17.290 max:17.585 mdev:0.272 8.8.4.4 min:15.392 avg:15.856 max:16.284 mdev:0.365

    Notes:

    • Use of the strict and warnings pragmata. Highly recommended; compulsory in my opinion.
    • A more succinct method for creating the command.
    • Capturing the result as a single string which is subsequently processed by Opening a filehandle into an in-memory scalar.
    • A simplified regex for use on an entire ping result. The four (min/avg/max/mdev) values are captured as a single entity and later split when the individual values are required.
    • A format for standardised report output; used later by printf.
    • Reading individual ping results by manipulating $/ (see "perlvar: Variables related to filehandles").

    I've shown a number of different ways to do various things. Pick and choose the bits you want; or, use as is, if you like all of it.

    — Ken