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

Hell Perl Monks,

Is there a way, in the code below, to print the number of answers returned for the query and reply sections, before printing the actual results? I'm sure there is but i am struggling to figure it out.

For the query section, I know my data is in $query->answer->rdatastr and i would like to get the total scalar count

For the reply section, I know the data is in $reply->answer->address and i would like to get the total scalar count

use warnings; use strict; use Net::DNS; use Data::Dumper::Simple; my $dnsserver; my @resolver; @resolver = qw(192.168.10.98); foreach $dnsserver (@resolver) { print "\n\nValidating Entries against $dnsserver\n"; my $resolver = Net::DNS::Resolver->new( nameservers =>$dnsserver, retry => 2, #recurse => 0, #debug => 1, ); my $ip = "192.168.50.70"; my $target_ip = join('.', reverse split(/\./, $ip)).".in-addr.arpa +"; my $query = $resolver->query($target_ip, 'PTR'); my $reply = $resolver->search("garytest.fedlab.local",'A'); warn Dumper(\$query, \$reply); if ($query) { # Print total count Here before Dispalying # Print "Total # of Hostnames found: ",(code) , "\n"; print "\n\nHost names found......\n"; foreach my $answer ($query->answer) { my $value=$answer->rdatastr; chop $value if ($value =~/.$/); print "\t", $value, "\n"; } } else { print "\tReverse Lookup failed: ", $resolver->errorstring, "\n"; } if ($reply) { # Print total count Here before Dispalying # Print "Total # of IP found: ",(code) , "\n"; # print "\n\nIP Address Found found......\n"; foreach my $rr ($reply->answer) { print "\t", $rr->address, "\n"; } } else { warn "\tForward Lookup Failed: ", $resolver->errorstring, +"\n"; } }

OUTPUT.......With Dumper

Z:\>perl net_dns.pl Validating Entries against 192.168.10.98 $query = bless( { 'replyfrom' => '192.168.10.98', 'status' => 34176, 'authority' => [], 'answer' => [ bless( { 'ptrdname' => bless( { 'label +' => [ + 'garytest_dup3' + ] }, 'Net: +:DNS::DomainName1035' ), 'owner' => bless( { 'label' = +> [], 'origin' +=> bless( { + 'label' => [ + '70', + '50', + '168', + '192', + 'in-addr', + 'arpa' + ] + }, 'Net::DNS::DomainName' ) }, 'Net::DN +S::DomainName1035' ), 'rdlength' => 15, 'class' => 1, 'type' => 12, 'ttl' => 3600 }, 'Net::DNS::RR::PTR' ), bless( { 'rdlength' => 23, 'owner' => bless( { 'label' = +> [], 'origin' +=> $query->{'answer'}[0]{'owner'}{'origin'} }, 'Net::DN +S::DomainName1035' ), 'ttl' => 3600, 'class' => 1, 'type' => 12, 'ptrdname' => bless( { 'label +' => [ + 'garytest', + 'fedlab', + 'local' + ] }, 'Net: +:DNS::DomainName1035' ) }, 'Net::DNS::RR::PTR' ), bless( { 'rdlength' => 16, 'owner' => bless( { 'label' = +> [], 'origin' +=> $query->{'answer'}[0]{'owner'}{'origin'} }, 'Net::DN +S::DomainName1035' ), 'ttl' => 3600, 'type' => 12, 'class' => 1, 'ptrdname' => bless( { 'origi +n' => bless( { + 'label' => [ + 'fedlab', + 'local' + ] + }, 'Net::DNS::DomainName' ), 'label +' => [ + 'garytest_dup2' + ] }, 'Net: +:DNS::DomainName1035' ) }, 'Net::DNS::RR::PTR' ), bless( { 'ptrdname' => bless( { 'origi +n' => $query->{'answer'}[2]{'ptrdname'}{'origin'}, 'label +' => [ + 'garytest_dup4' + ] }, 'Net: +:DNS::DomainName1035' ), 'ttl' => 3600, 'class' => 1, 'type' => 12, 'rdlength' => 16, 'owner' => bless( { 'origin' +=> $query->{'answer'}[0]{'owner'}{'origin'}, 'label' = +> [] }, 'Net::DN +S::DomainName1035' ) }, 'Net::DNS::RR::PTR' ), bless( { 'ttl' => 3600, 'class' => 1, 'type' => 12, 'rdlength' => 14, 'owner' => bless( { 'origin' +=> $query->{'answer'}[0]{'owner'}{'origin'}, 'label' = +> [] }, 'Net::DN +S::DomainName1035' ), 'ptrdname' => bless( { 'label +' => [ + 'garytest_dup' + ] }, 'Net: +:DNS::DomainName1035' ) }, 'Net::DNS::RR::PTR' ) ], 'question' => [ bless( { 'qclass' => 1, 'qname' => bless( { 'label' + => [ + '70', + '50', + '168', + '192', + 'in-addr', + 'arpa' + ] }, 'Net:: +DNS::DomainName1035' ), 'qtype' => 12 }, 'Net::DNS::Question' ) ], 'count' => [ 1, 5, 0, 0 ], 'additional' => [], 'id' => 55726, 'xedns' => bless( { 'owner' => bless( { 'label' => [ +] }, 'Net::DNS:: +DomainName1035' ), 'type' => 41 }, 'Net::DNS::RR::OPT' ), 'replysize' => 188 }, 'Net::DNS::Packet' ); $reply = bless( { 'replysize' => 87, 'xedns' => bless( { 'owner' => $query->{'xedns'}{'ow +ner'}, 'type' => 41 }, 'Net::DNS::RR::OPT' ), 'additional' => [], 'id' => 40872, 'count' => [ 1, 3, 0, 0 ], 'question' => [ bless( { 'qtype' => 1, 'qname' => bless( { 'label' + => [ + 'garytest', + 'fedlab', + 'local' + ] }, 'Net:: +DNS::DomainName1035' ), 'qclass' => 1 }, 'Net::DNS::Question' ) ], 'authority' => [], 'answer' => [ bless( { 'address' => '+¿2G', 'ttl' => 3600, 'class' => 1, 'type' => 1, 'rdlength' => 4, 'owner' => bless( { 'origin' +=> bless( { + 'label' => [ + 'garytest', + 'fedlab', + 'local' + ] + }, 'Net::DNS::DomainName' ), 'label' = +> [] }, 'Net::DN +S::DomainName1035' ) }, 'Net::DNS::RR::A' ), bless( { 'type' => 1, 'class' => 1, 'ttl' => 3600, 'owner' => bless( { 'origin' +=> $reply->{'answer'}[0]{'owner'}{'origin'}, 'label' = +> [] }, 'Net::DN +S::DomainName1035' ), 'rdlength' => 4, 'address' => '+¿2F' }, 'Net::DNS::RR::A' ), bless( { 'rdlength' => 4, 'owner' => bless( { 'origin' +=> $reply->{'answer'}[0]{'owner'}{'origin'}, 'label' = +> [] }, 'Net::DN +S::DomainName1035' ), 'ttl' => 3600, 'type' => 1, 'class' => 1, 'address' => '+¿2H' }, 'Net::DNS::RR::A' ) ], 'status' => 34176, 'replyfrom' => '192.168.10.98' }, 'Net::DNS::Packet' ); Host names found...... garytest_dup3 garytest.fedlab.local garytest_dup2.fedlab.local garytest_dup4.fedlab.local garytest_dup IP Address Found found...... 192.168.50.71 192.168.50.70 192.168.50.72

Replies are listed 'Best First'.
Re: Net::DNS answer count
by kcott (Archbishop) on Apr 08, 2023 at 22:19 UTC

    G'day g_speran,

    The values of both answer keys ($query->{answer} & $reply->{answer}) are arrayrefs. Counting the number of elements in an arrayref is very simple:

    $ perl -E 'my $answer = [qw{x y z}]; say 0+@$answer;' 3

    You might benefit from reading "perldsc - Perl Data Structures Cookbook".

    — Ken

      So I got the code to work by doing the following:

      Is there a better way to do this with the code originally posted?

      my $query = $resolver->query($target_ip, 'PTR'); my @answer = $query->answer; print "Total # of Hostnames found: ", scalar(@answer), "\n";
        "Is there a better way to do this with the code originally posted?"

        In essence, no.

        You want the value of @answer in the loop just after reporting the count, so it makes sense to assign 'my @answer =' once and then reuse the value.

        If you're asking specifically about the print statement, what you have is fine. There are multiple ways of doing this and, for the most part, it would come down to personal style more than anything else. I tend to favour '0+@array' over 'scalar(@array)'; but that's just me. Here's a handful of different ways you could do this:

        $ perl -E ' my @answers = qw{x y z}; say "Total: ", scalar(@answers); say "Total: ", 0+@answers; say "Total: @{[scalar @answers]}"; say "Total: @{[0+@answers]}"; ' Total: 3 Total: 3 Total: 3 Total: 3

        [See "discussion of @{[...]}" (from a few days ago) if you're unfamiliar with that construct.]

        In the main, your code is fine.

        There's one subtlety of which few are aware (or fully understand) — don't feel too bad about this slip-up by yourself. :-)

        The loop variable in a for loop is lexically scoped to that loop. Other references to variables of the same name outside the loop are different variables. Consider these (and note that I had to turn off strict for demonstration purposes):

        $ perl -E ' #use strict; use warnings; { my $i; for $i (1,2,3) { say $i; } say $i // "undefined"; } { for my $i (1,2,3) { say $i; } say $i // "undefined"; } { for $i (1,2,3) { say $i; } say $i // "undefined"; } { my $i = q{completely different $i}; for $i (1,2,3) { say $i; } say $i // "undefined"; } ' 1 2 3 undefined 1 2 3 undefined 1 2 3 undefined 1 2 3 completely different $i

        With strict enabled, the $i outside the loop is problematic:

        $ perl -E ' use strict; use warnings; for my $i (1,2,3) { say $i; } say $i // "undefined"; ' Global symbol "$i" requires explicit package name (did you forget to d +eclare "my $i"?) at -e line 9. Execution of -e aborted due to compilation errors.

        There is an anomaly with this. I consider it to be a bug in Perl and probably the cause of much confusion. If you don't declare the loop variable with my, strict complains:

        $ perl -E ' use strict; use warnings; for $i (1,2,3) { say $i; } ' Global symbol "$i" requires explicit package name (did you forget to d +eclare "my $i"?) at -e line 5. Global symbol "$i" requires explicit package name (did you forget to d +eclare "my $i"?) at -e line 6. Execution of -e aborted due to compilation errors.

        unless you've previously declared a different variable with the same name with my in an outer scope:

        $ perl -E ' use strict; use warnings; my $i; for $i (1,2,3) { say $i; } say $i // "undefined"; ' 1 2 3 undefined

        Declaration after the loop still raises an error with the loop variable:

        $ perl -E ' use strict; use warnings; for $i (1,2,3) { say $i; } my $i; say $i // "undefined"; ' Global symbol "$i" requires explicit package name (did you forget to d +eclare "my $i"?) at -e line 5. Global symbol "$i" requires explicit package name (did you forget to d +eclare "my $i"?) at -e line 6. Execution of -e aborted due to compilation errors.

        Here's a very quick review of the remainder; bear in mind that much of this comes down to style as opposed to corrections:

        • Using a plural for arrays generally reads better: $answer vs. @answers. Also, @answer could be a simple typo but strict won't pick up that you really meant $answer.
        • Although $object->method is valid syntax, $object->method() is clearer in my opinion.
        • You generally want chomp instead of chop.
        • Don't bother chomping when the next line is print (now requiring that the newline be added back again). In other words, don't write all of this:
          chomp $line; print $line, "\n";

          when this would've sufficed:

          print $line;
        • for and foreach are synonymous. Using the former promotes (good) laziness.
        • Consistent code layout improves readability. What you have isn't terrible; although, I do see indentations of 2, 4, 5 & 8 characters.

        — Ken