Bulk check domains avability with Perl - perl

I need to check a list of domain names and get the list of domains with no NS (probably unregistered). I've already found a nice solution with ADNS and adnshost. The command "adnshost -a -tns domain.com" does what I need (The 4th column in the output will contain the result) but I want to achieve the same with Perl and Net::DNS::Async.
My code:
#!/usr/bin/perl -w
use strict;
use utf8;
use Net::DNS::Async;
my $c = new Net::DNS::Async(QueueSize => 1000, Retries => 3);
my $filename = 'domain_list.txt';
open(FH, '<', $filename);
while(<FH>){
chomp($url);
$c->add(\&callback, "$url");
}
$c->await();
sub callback {
my $response = shift;
print $response->string;
}
So, how do I get the needed info with Perl and Net::DNS::Async ?

You can add NS to the add arguments.
while (<FH>) {
chomp;
$c->add(\&callback, $_, 'NS');
}
$c->await();
sub callback {
my $response = shift;
unless ($response->answer) {
my $host = join '.', #{$response->{question}[0]{qname}{label}};
print "$host\n";
}
}
$response->answer will be "empty" if there are no replies.

Related

include/eval perl file into unique namespace defined at runtime

I'm writing a tool that must import a number of other perl config files. The files are not wrapped w/packages and may have similar or conflicting variables/functions. I don't have the ability to change the format of these files, so I must work around what they are. What I was thinking to do was import each into a unique name space, but I've not found a way to do that using do, require, or use. If I don't use dynamic names, just a hardcoded name, I can do it.
Want something like this:
sub sourceTheFile {
my ($namespace, $file) = #_;
package $namespace;
do $file;
1;
return;
}
That doesn't work because the package command requires a constant for the name. So then I try something like this:
sub sourceTheFile {
my ($namespace, $file) = #_;
eval "package $namespace;do $file;1;"
return;
}
But the contents of the file read by do are placed in the main:: scope not the one I want. The target scope is created, just not populated by the
do. (I tried require, and just a straight cat $file inside the eval as well.)
I'm using Devel::Symdump to verify that the namespaces are built correctly or not.
example input file:
my $xyz = "some var";
%all_have_this = ( common=>"stuff" );
ADDITIONAL CHALLENGE
Using the answer that does the temp file build and do call, I can make this work dynamically as I require. BUT, big but, how do I now reference the data inside this new namespace? Perl doesn't seem to have the lose ability to build a variable name from a string and use that as the variable.
I am not sure why the eval did not work. Maybe a bug? Here is a workaround using a temp file. This works for me:
use strict;
use warnings;
use Devel::Symdump;
use File::Temp;
my $file = './test.pl';
my $namespace = 'TEST';
{
my $fh = File::Temp->new();
print $fh "package $namespace;\n";
print $fh "do '$file';\n";
print $fh "1;\n";
close $fh;
do $fh->filename;
}
Perl's use and require facilities make use of any hooks you might have installed in #INC. You can simply install a hook which looks in a specific location to load modules with a prefix you choose:
package MyIncHook;
use strict;
use warnings;
use autouse Carp => qw( croak );
use File::Spec::Functions qw( catfile );
sub import {
my ($class, $prefix, $location) = #_;
unshift #INC, _loader_for($prefix, $location);
return;
}
sub _loader_for {
my $prefix = shift;
my $location = shift;
$prefix =~ s{::}{/}g;
return sub {
my $self = shift;
my $wanted = shift;
return unless $wanted =~ /^\Q$prefix/;
my $path = catfile($location, $wanted);
my ($is_done);
open my $fh, '<', $path
or croak "Failed to open '$path' for reading: $!";
my $loader = sub {
if ($is_done) {
close $fh
or croak "Failed to close '$path': $!";
return 0;
}
if (defined (my $line = <$fh>)) {
$_ = $line;
return 1;
}
else {
$_ = "1\n";
$is_done = 1;
return 1;
}
};
(my $package = $wanted) =~ s{/}{::}g;
$package =~ s/[.]pm\z//;
my #ret = (\"package $package;", $loader);
return #ret;
}
}
__PACKAGE__;
__END__
Obviously, modify the construction of $path according to your requirements.
You can use it like this:
#!/usr/bin/env perl
use strict;
use warnings;
use MyIncHook ('My::Namespace', "$ENV{TEMP}/1");
use My::Namespace::Rand;
print $My::Namespace::Rand::settings{WARNING_LEVEL}, "\n";
where $ENV{TEMP}/1/My/Namespace/Rand.pm contains:
%settings = (
WARNING_LEVEL => 'critical',
);
Output:
C:\Temp> perl t.pl
critical
You can, obviously, define your own mapping from made up module names to file names.

Read ini files without section names

I want to make a configuration file which hold some objects, like this (where of course none of the paramaters can be considered as a primary key)
param1=abc
param2=ghj
param1=bcd
param2=hjk
; always the sames parameters
This file could be read, lets say with Config::IniFiles, because it has a direct transcription into ini file, like this
[0]
param1=abc
param2=ghj
[1]
param1=bcd
param2=hjk
with, for example, something like
perl -pe 'if (m/^\s*$/ || !$section ) print "[", ($section++ || 0) , "]"'
And finish with
open my $fh, '<', "/path/to/config_file.ini" or die $!;
$cfg = Config::IniFiles->new( -file => $fh );
(...parse here the sections starting with 0.)
But, I here ask me some question about the thing becoming quite complex....
(A) Is There a way to transform the $fh, so that it is not required to execute the perl one-liner BEFORE reading the file sequentially? So, to transform the file during perl is actually reading it.
or
(B) Is there a module to read my wonderfull flat database? Or something approching? I let myslef said, that Gnu coreutils does this kind of flat file reading, but I cannot remember how.
You can create a simple subclass of Config::INI::Reader:
package MyReader;
use strict;
use warnings;
use base 'Config::INI::Reader';
sub new {
my $class = shift;
my $self = $class->SUPER::new( #_ );
$self->{section} = 0;
return $self;
}
sub starting_section { 0 };
sub can_ignore { 0 };
sub parse_section_header {
my ( $self, $line ) = #_;
return $line =~ /^\s*$/ ? ++$self->{section} : undef ;
}
1;
With your input this gives:
% perl -MMyReader -MData::Dumper -e 'print Dumper( MyReader->read_file("cfg") )'
$VAR1 = {
'1' => {
'param2' => 'hjk',
'param1' => 'bcd'
},
'0' => {
'param2' => 'ghj',
'param1' => 'abc'
}
};
You can use a variable reference instead of a file name to create a filehandle that reads from it:
use strict;
use warnings;
use autodie;
my $config = "/path/to/config_file.ini";
my $content = do {
local $/;
open my $fh, "<", $config;
"\n". <$fh>;
};
# one liner replacement
my $section = 0;
$content =~ s/^\s*$/ "\n[". $section++ ."]" /mge;
open my $fh, '<', \$content;
my $cfg = Config::IniFiles->new( -file => $fh );
# ...
You can store the modified data in a real file or a string variable, but I suggest that you use paragraph mode by setting the input record separator $/ to the empty string. Like this
use strict;
use warnings;
{
local $/ = ''; # Read file in "paragraphs"
my $section = 0;
while (<DATA>) {
printf "[%d]\n", $section++;
print;
}
}
__DATA__
param1=abc
param2=ghj
param1=bcd
param2=hjk
output
[0]
param1=abc
param2=ghj
[1]
param1=bcd
param2=hjk
Update
If you read the file into a string, adding section identifiers as above, then you can read the result directly into a Config::IniFiles object using a string reference, for instance
my $config = Config::IniFiles->new(-file => \$modified_contents)
This example shows the tie interface, which results in a Perl hash that contains the configuration information. I have used Data::Dump only to show the structure of the resultant hash.
use strict;
use warnings;
use Config::IniFiles;
my $config;
{
open my $fh, '<', 'config_file.ini' or die "Couldn't open config file: $!";
my $section = 0;
local $/ = '';
while (<$fh>) {
$config .= sprintf "[%d]\n", $section++;
$config .= $_;
}
};
tie my %config, 'Config::IniFiles', -file => \$config;
use Data::Dump;
dd \%config;
output
{
# tied Config::IniFiles
"0" => {
# tied Config::IniFiles::_section
param1 => "abc",
param2 => "ghj",
},
"1" => {
# tied Config::IniFiles::_section
param1 => "bcd",
param2 => "hjk",
},
}
You may want to perform operations on a flux of objects (as Powershell) instead of a flux of text, so
use strict;
use warnings;
use English;
sub operation {
# do something with objects
...
}
{
local $INPUT_RECORD_SEPARATOR = '';
# object are separated with empty lines
while (<STDIN>) {
# key value
my %object = ( m/^ ([^=]+) = ([[:print:]]*) $ /xmsg );
# key cannot have = included, which is the delimiter
# value are printable characters (one line only)
operation ( \%object )
}
A like also other answers.

Learning the High Order Perl: issue with iterator

I study the High Order Perl book and have an issue with iterators in the Chapter 4.3.4.
The code:
main_script.pl
#!/perl
use strict;
use warnings;
use FindBin qw($Bin);
use lib $Bin;
use Iterator_Utils qw(:all);
use FlatDB;
my $db = FlatDB->new("$Bin/db.csv") or die "$!";
my $q = $db->query('STATE', 'NY');
while (my $rec = NEXTVAL($q) )
{
print $rec;
}
Iterator_Utils.pm
#!/perl
use strict;
use warnings;
package Iterator_Utils;
use Exporter 'import';;
our #EXPORT_OK = qw(NEXTVAL Iterator
append imap igrep
iterate_function filehandle_iterator list_iterator);
our %EXPORT_TAGS = ('all' => \#EXPORT_OK);
sub NEXTVAL { $_[0]->() }
sub Iterator (&) { return $_[0] }
FlatDB.pm
#!/perl
use strict;
use warnings;
package FlatDB;
my $FIELDSEP = qr/:/;
sub new
{
my $class = shift;
my $file = shift;
open my $fh, "<", $file or return;
chomp(my $schema = <$fh>);
my #field = split $FIELDSEP, $schema;
my %fieldnum = map { uc $field[$_] => $_ } (0..$#field);
bless
{
FH => $fh,
FIELDS => \#field,
FIELDNUM => \%fieldnum,
FIELDSEP => $FIELDSEP
} => $class;
}
use Fcntl ':seek';
sub query
{
my $self = shift;
my ($field, $value) = #_;
my $fieldnum = $self->{FIELDNUM}{uc $field};
return unless defined $fieldnum;
my $fh = $self->{FH};
seek $fh, 0, SEEK_SET;
<$fh>; # discard schema line
return Iterator
{
local $_;
while (<$fh>)
{
chomp;
my #fields = split $self->{FIELDSEP}, $_, -1;
my $fieldval = $fields[$fieldnum];
return $_ if $fieldval eq $value;
}
return;
};
}
db.csv
LASTNAME:FIRSTNAME:CITY:STATE:OWES
Adler:David:New York:NY:157.00
Ashton:Elaine:Boston:MA:0.00
Dominus:Mark:Philadelphia:PA:0.00
Orwant:Jon:Cambridge:MA:26.30
Schwern:Michael:New York:NY:149658.23
Wall:Larry:Mountain View:CA:-372.14
Just as in the book so far, right? However I do not get the output (the strings with Adler and Schwern should occur). The error message is:
Can't use string ("Adler:David:New York:NY:157.00") as a subroutine ref while
"strict refs" in use at N:/Perle/Learn/Iterators/Iterator_Utils.pm line 12, <$fh>
line 3.
What am I doing wrong?
Thanks in advance!
FlatDB calls Iterator, which is defined in Iterator_Utils, so it needs to import that function from Iterator_Utils. If you add
use Iterator_Utils qw(Iterator);
after package FlatDB, the program will work.
Thanks very much for finding this error. I will add this to the errata on the web site. If you would like to be credited by name, please email me your name.

How to add one more node information to xml file

I written one script that create one xml file from multiple files,I written script like this.
#!/usr/bin/perl
use warnings;
use strict;
use XML::LibXML;
use Carp;
use File::Find;
use File::Spec::Functions qw( canonpath );
use XML::LibXML::Reader;
use Digest::MD5 'md5';
if ( #ARGV == 0 ) {
push #ARGV, "c:/main/work";
warn "Using default path $ARGV[0]\n Usage: $0 path ...\n";
}
open( my $allxml, '>', "all_xml_contents.combined.xml" )
or die "can't open output xml file for writing: $!\n";
print $allxml '<?xml version="1.0" encoding="UTF-8"?>',
"\n<Shiporder xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n";
my %shipto_md5;
find(
sub {
return unless ( /(_stc\.xml)$/ and -f );
extract_information();
return;
},
#ARGV
);
print $allxml "</Shiporder>\n";
sub extract_information {
my $path = $_;
if ( my $reader = XML::LibXML::Reader->new( location => $path )) {
while ( $reader->nextElement( 'data' )) {
my $elem = $reader->readOuterXml();
my $md5 = md5( $elem );
print $allxml $reader->readOuterXml() unless ( $shipto_md5{$md5}++ );
}
}
return;
}
from above script I am extracting data node information from all xml files and stored in a new xml file . but I have one more node starts with "details", I need to extract that information and I need to add that information also to the file, I tried like this
$reader->nextElement( 'details' );
my $information = $reader->readOuterXml();
I added this in while loop but how can I assign or print this data into same file($all xml). Please help me with this problem.
After your suggestion I tried like this, It gives error
#!/usr/bin/perl
use warnings;
use strict;
use XML::LibXML;
use Carp;
use File::Find;
use File::Spec::Functions qw( canonpath );
use XML::LibXML::Reader;
if ( #ARGV == 0 ) {
push #ARGV, "V:/main/work";
warn "Using default path $ARGV[0]\n Usage: $0 path ...\n";
}
my $libXML = new XML::LibXML;
my $outputDom = $libXML->parse_string('<?xml version="1.0" encoding="UTF-8"?
>','<Shiporder xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">');
my $shiporder = $outputDom->documentElement;
find(
sub {
return unless ( /(_stc\.xml)$/ and -f );
extract_information();
return;
},
#ARGV
);
sub extract_information {
my $path = $_;
if(my #inputDom = XML::LibXML->load_xml(location => $path)){
$inputDom->findnodes('//data || //deatils');
foreach (#$inputDom) {
$shiporder->appendChild($_->parentNode->cloneNode(1));
}
$outputDom->toFile("allfiles.xml");
}
}
but it gives like " '\n\n:1: Parser error:Strat tag expected,'<' not found " Can you help me with script because I am very new to perl.
You would do a lot better if you used what XML::LibXML and related modules gives you, it is a very large and comprehensive module and allows you to do a lot in few lines.
You can use the parser to start a new dom document using parse_string, storing the root node using documentElement. From there, use parse_file to load up each of your input files, then findnodes on the input files to extract the nodes you want to clone. Then append a clone of your input nodes to the output document, and finally use the toFile method to write out your output.
Something like:
my $libXML = new XML::LibXML;
my $outputDom = $libXML->parse_string('<?xml version="1.0" encoding="UTF-8"?>',
'\n<Shiporder xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">\n');
my $shiporder = $outputDom->documentElement;
...
my $inputDom = $libXML->parse_file(some_file_name);
$inputDom->findnodes('//data || //details'); # use a more suitable xpath
foreach (#$inputDom) {
$shipOrder->appendChild($_->parentNode->cloneNode(1)); # if you want parent too...
}
...
$outputDom->toFile(some_output_file);
}
You will have to allow for namespaces and whatnot, but this gives one approach to start with.

How can match the first value of #ARGV to an array of possible options

I am trying to figure a way to capture the first argument from #ARGV and check its validity by checking it against an array of known valid arguments. I thought I could do this with a simple foreach loop but I realized this won't work because it will fail when the first invalid match comes back, which for my example script is the second argument.
Here the code that pertains to the problem, its concept script so there is not much.
my $primary_mode = $ARGV[0];
primary_mode_check($primary_mode);
sub primary_mode_check {
my #program_modes = ('create', 'destroy');
my $selected_mode = shift;
foreach my $mode (#program_modes) {
if ($selected_mode ne $mode) {
die RED "\'$selected_mode\' is not a valid program mode!\n";
}
}
}
Is there another way to accomplish the same idea? I am already using Getopt::Long in combonation with #ARGV to achieve a certain style. So I am focused on wanting to make this work.
UPDATE
I was thinking maybe I could match against regex, is that a possibility?
my $primary_mode = $ARGV[0] or die "No mode provided";
primary_mode_check($primary_mode);
sub primary_mode_check {
my $selected_mode = shift;
my #program_modes = ('create', 'destroy');
die "'$selected_mode' is not a valid program mode!\n"
unless grep { $selected_mode eq $_ } #program_modes;
}
If you are using perl 5.10 or greater:
use v5.10;
my $primary_mode = $ARGV[0] or die "No mode provided";
my #program_modes = qw(create destroy);
die "'$selected_mode' is not a valid program mode!\n"
unless $primary_mode ~~ #program_modes;
You code: Die if the arg doesn't match one of the allowed modes.
You want: Die if the arg doesn't match any of the allowed modes.
Put differently: Don't die if the arg matches one of the allowed modes.
my #program_modes = qw( create destroy );
sub primary_mode_check {
my ($selected_mode) = #_;
for my $mode (#program_modes) {
return if $selected_mode eq $mode;
}
die "'$selected_mode' is not a valid program mode!\n";
}
But a hash simplifies things a bit.
my %program_modes = map { $_ => 1 } qw( create destroy );
sub primary_mode_check {
my ($selected_mode) = #_;
die "'$selected_mode' is not a valid program mode!\n"
if !$program_modes{$selected_mode};
}
You might find App::Cmd useful for easy writing of application with commands.
I would recommend going with a hash of allowed modes. In addition, pass the allowed modes to the function rather than embedding them in the function.
You can also use grep for this purpose if the allowed modes are in an array:
#!/usr/bin/env perl
use warnings; use strict;
my ($primary_mode) = #ARGV;
my $allowed_modes = [qw(create destroy)];
check_primary_mode($primary_mode, $allowed_modes)
or die sprintf "%s is not a valid program mode\n", $primary_mode;
sub check_primary_mode {
my ($mode, $allowed) = #_;
return grep $mode eq $_, #$allowed;
}
However, grep will go through the entire array even though we need just one match. List::MoreUtils::first_index will short-circuit once a match is found (it does not matter if you have only two possible modes, but in the more general case, it might):
use List::MoreUtils qw( first_index );
...
sub check_primary_mode {
my ($mode, $allowed) = #_;
return (0 <= first_index { $mode eq $_ } #$allowed);
}
my $primary_mode = $ARGV[0];
primary_mode_check($primary_mode);
sub primary_mode_check {
my %program_modes; #program_modes{qw(create destroy)}=();
my $selected_mode = shift;
die RED "\'$selected_mode\' is not a valid program mode!\n"
unless exists $program_modes{$selected_mode};
}
I often use this idiom in such a case:
use strict;
use warnings;
my $cmd = shift #ARGV;
my #allowed = qw/ install uninstall check purge /;
die "Cannot understand command" unless ( grep { $cmd eq $_ } #allowed );
Edit: reading more carefully it looks quite a bit like Sinan's, and he's right, using first would search faster in a large list of possible ops.
Yes, a regular expression should work. For example:
my #modes = ('create', 'destroy');
my $regexp = join "|", #modes;
if ($selected =~ /^(?:$regexp)\z/) {
print "Found program mode '$1'\n";
} else {
die RED "\'$selected\' is not a valid program mode!\n";
}