How to use GetOptions to detect trailing strings? - perl

I am absolutely new to Perl and I am trying to figure out a problem with Perl script parsing script arguments.
I have the following Perl script called sample-perl.pl:
use strict;
use warnings;
use 5.010;
use Getopt::Long qw(GetOptions);
my $source_address;
my $dest_address;
GetOptions('from=s' => \$source_address,
'to=s' => \$dest_address) or die "Usage: $0 --from NAME --to NAME\n";
if ($source_address) {
say $source_address;
}
if ($dest_address) {
say $dest_address;
}
And if I use a command like (where I forgot to enter the second option):
perl sample-perl.pl --from nyc lon
Output will be: nyc
How can I enforce that if there is an additional string at the end, it is detected and an error is displayed instead?
Solution:
adding this will help my case at least:
if(#ARGV){
//throw error
}

After calling GetOptions, check for any remaining command line options in the #ARGV array. This assumes that all unexpected arguments will generate an error:
use strict;
use warnings;
use 5.010;
use Getopt::Long qw(GetOptions);
my $source_address;
my $dest_address;
GetOptions('from=s' => \$source_address,
'to=s' => \$dest_address) or die "Usage: $0 --from NAME --to NAME\n";
#ARGV and die "Error: unexpected args: #ARGV";
if ($source_address) {
say $source_address;
}
if ($dest_address) {
say $dest_address;
}

I was busy answered and I see it is now answered, just some additional info.
use strict;
use warnings;
use 5.010;
use Getopt::Long qw(GetOptions);
my $source_address;
my $dest_address;
GetOptions('from=s' => \$source_address,
'to=s' => \$dest_address) or die "Usage: $0 --from NAME --to NAME\n";
#ARGV and die "To many arguments after --from or --to : #ARGV ";
if ($source_address) {
say $source_address;
} else {
say "Error: No Source specified"; #Check to see if --from is actually specified, else print error.
}
if ($dest_address) {
say $dest_address;
} else {
say "Error: No destination specified"; #Check to see if --to is actually specified, else print error.
}
so in short

Related

Why the following code shows an error?

I want to pass arguments from the command line so i had tried the following code but it throws an error?
use strict;
use warnings;
use Getopt::Long qw(GetOptions);
use Getopt::Std;
print "raw data:#ARGV\n";
my $site=getopts('bangalore','norwood','limerick');
if($site)
{
print "success";
}
else
{
die "error";
}
print "final data:#ARGV \n";
Your code is not correct. Please go through the documentation first: http://perldoc.perl.org/Getopt/Long.html
Below is an attempt to guess what you were trying to achieve.
use strict;
use warnings;
use Getopt::Long qw(GetOptions);
my $city;
print "raw data:#ARGV\n";
GetOptions ("city=s" => \$city) or die("Error in command line arguments\n");
my $site = $city;
if($site){
print "success: City is $city\n";
}
print "Final data:#ARGV \n";
Output:
chankeypathak#stackoverflow:~/Desktop$ perl test.pl -city=bangalore
raw data:-city=bangalore
success: City is bangalore
Final data:
Output when passed incorrect param:
chankeypathak#stackoverflow:~/Desktop$ perl test.pl -blah=blah
raw data:-blah=blah
Unknown option: blah
Error in command line arguments
Thanks to: alvinalexander
#!/usr/bin/perl -w
# a perl getopts example
# alvin alexander, http://www.devdaily.com
use strict;
use Getopt::Std;
# declare the perl command line flags/options we want to allow
my %options=();
getopts("hj:ln:s:", \%options);
# test for the existence of the options on the command line.
# in a normal program you'd do more than just print these.
print "-h $options{h}\n" if defined $options{h};
print "-j $options{j}\n" if defined $options{j};
print "-l $options{l}\n" if defined $options{l};
print "-n $options{n}\n" if defined $options{n};
print "-s $options{s}\n" if defined $options{s};
# other things found on the command line
print "Other things found on the command line:\n" if $ARGV[0];
foreach (#ARGV)
{
print "$_\n";
}

Unknown options in perl Getopt::Long

How can you recognize unknown options using Getopt::Long ?
I tried '<>', but it did not work as expected..Consider:
use Modern::Perl;
use Getopt::Long;
my $help='';
GetOptions ('help' => \$help,'<>' => \&usage);
usage() if $help;
usage() if #ARGV != 1;
my $fn=pop;
say "FileName: $fn";
sub usage {
say "Unknown option: #_" if ( #_ );
say "Usage: $0 <filename>";
say " $0 --help";
say "";
exit
}
I would like to print Unknown option only if there is an unrecognized option ( in this case, anything other then --help) . But now it thinks the filename is an unrecognized option..
Call your usage function if GetOptions fails. Getopt::Long will print Unknown option for you (to STDERR):
use Modern::Perl;
use Getopt::Long;
my $help='';
GetOptions ('help' => \$help) or usage();
usage() if $help;
usage() if #ARGV != 1;
my $fn=pop;
say "FileName: $fn";
sub usage {
say "Usage: $0 <filename>";
say " $0 --help";
say "";
exit
}
Start using pod documentation as the core modules Getopt::Long and Pod::Usage work very well together. Can get the behavior you want without having to create helper methods to accomplish it:
Here's an example script:
#!/usr/bin/perl
use File::Basename qw(basename);
use Getopt::Long qw(GetOptions);
use Pod::Usage qw(pod2usage);
use Readonly;
use version;
use strict;
use warnings;
Readonly my $VERSION => qv('0.0.1');
Readonly my $EXE => basename($0);
GetOptions(
'version' => \my $version,
'usage' => \my $usage,
'help|?' => \my $help,
'man' => \my $man,
) or pod2usage(-verbose => 0);
pod2usage(-verbose => 0) if $usage;
pod2usage(-verbose => 1) if $help;
pod2usage(-verbose => 2) if $man;
if ($version) {
print "$EXE v$VERSION\n";
exit;
}
## Check for File
pod2usage("$EXE: No filename specified.\n") unless #ARGV;
my $file = $ARGV[0];
pod2usage("$EXE: $file is a directory.\n") if -d $file;
pod2usage("$EXE: $file is not writable.\n") if !-w $file;
#....
print "Hello World\n";
#....
1;
__END__
=head1 NAME
hello.pl - Mirrors a script using pod
=head1 SYNOPSIS
./hello.pl [FILE]
=head1 OPTIONS
=over 4
=item --version
Print the version information
=item --usage
Print the usage line of this summary
=item --help
Print this summary.
=item --man
Print the complete manpage
=back
=head1 DESCRIPTION
Sometimes a programmer just enjoys a bit of documentation.
They can't help themselves, it makes them feel accomplished.
=head1 AUTHOR
Written by A Simple Coder
Output:
>perl hello.pl --test
Unknown option: test
Usage:
./hello.pl [FILE]

Whole string not getting read while reading the file using Config::IniFiles

I am unable to print the whole lines as i try and parse the ini file using Config:Ini operation, its the last part where I believed that the array will have the whole line and not only the key, I am surely missing something here
Input
[DomainCredentials]
broker=SERVER
domain=CUSTOMER1
[ProviderCredentials]
Class=A
Routine=B
Code
#!/sbin/perl -w
use lib "/usr/lib/perl5/site_perl";
use lib "/usr/lib/perl5/vendor_perl";
use strict;
use warnings;
use Config::IniFiles;
my $sPPFile="/tmp/config.txt";
my $sysSec="DomainCredentials";
my $cfg = Config::IniFiles->new(-file=> $sPPFile) || die "Could open file $sPPFile\n";
if ($#){
print "Error";
exit 1;
}
my #params_provider = $cfg->Parameters("ProviderCredentials");
foreach (#params_provider){
print $_."\n";
}
Output
Class
Routine
Expected Output
Class=A
Routine=B
You could use the tied hash option of Config::IniFiles to get the config.txt parameter/value pairs:
use strict;
use warnings;
use Config::IniFiles;
my %ini;
my $sPPFile = "/tmp/config.txt";
tie %ini, 'Config::IniFiles', ( -file => $sPPFile );
print "$_=$ini{ProviderCredentials}{$_}\n"
for keys %{ $ini{ProviderCredentials} };
Output on your dataset:
Class=A
Routine=B
You can change the value of a parameter, and then update the config file by doing this:
$ini{ProviderCredentials}{Class} = 'C';
tied(%ini)->RewriteConfig();
The last command actually writes out the entire config held in the tied hash.
Hope this helps!
It looks like Parameters only returns keys.
You then have to use val to get the values.
#!/sbin/perl -w
use lib "/usr/lib/perl5/site_perl";
use lib "/usr/lib/perl5/vendor_perl";
use strict;
use warnings;
use Config::IniFiles;
my $sPPFile="/tmp/config.txt";
my $sysSec="DomainCredentials";
my $cfg = Config::IniFiles->new(-file=> $sPPFile) || die "Could open file $sPPFile\n";
if ($#){
print "Error";
exit 1;
}
my #param_arr = ('broker','domain');
my %param_hash;
foreach my $p (#param_arr){
if (defined $cfg->val("$sysSec",$p)){
$param_hash{$p} = $cfg->val("$sysSec",$p);
}
else{
die "Could not get parameter $p\n";
}
}
#print $param_hash{broker};
#print $param_hash{domain};
my #params_provider = $cfg->Parameters("ProviderCredentials");
if (defined $cfg->Parameters("ProviderCredentials")){
my #params_provider = $cfg->Parameters("ProviderCredentials");
}else{
die "Could not get parameter ProviderCredentials\n";
}
foreach (#params_provider){
print "Key : ".$_."\t Value : ".$cfg->val("ProviderCredentials",$_)."\n";
}

debugging perl script - variable interpolation

Try to debug this script. I think it maybe an issue of variable interpolation? I'm not sure.
It works using options if I pass the values like so:
perl test-file-exists.pl --file /proj/Output/20111126/_GOOD
I am trying to remove the option of passing in --file since I need to generate the date
dynamically.
perl test-file-exists.pl
Given the code changes below (I commented out the options piece). I am trying to create the string (see $chkfil). I am getting errors passing in $dt4. Somehow, its not passing in the file string that I am creating into this other module.
use strict;
use warnings;
use lib '/home/test/lib';
use ProxyCmd;
use Getopt::Long;
#
### Set up for Getopt
#
#my $chkfil;
#my $help;
#usage() if ( #ARGV < 1 or
# ! GetOptions('help|?' => \$help,
# 'file=s' => \$chkfil)
# or defined $help );
my $cmd = ProxyCmd->new( User=>"test_acct",
AuthToken=>"YToken",
loginServer=>"host.com");
# Get previous day
my $dt4 = qx {date --date='-1day' +'%Y%m%d'};
# Check file
my $chkfil = qq{/proj/Output/$dt4/_GOOD};
# Now test the fileExists function
print "Checking 'fileExists':\n";
my $feResults = $cmd->fileExists("$chkfil");
if ($feResults == 0) {
print "File Exists!\n";
} else {
print "File Does Not Exist\n";
}
sub usage
{
print "Unknown option: #_\n" if ( #_ );
print "usage: program [--file /proj/Output/20111126/_GOOD] [--help|-?]\n";
exit;
}
When you use backticks or qx, you get the trailing newline included so chomp it off:
my $dt4 = qx {date --date='-1day' +'%Y%m%d'};
chomp $dt4;
and you'll get a sensible filename.
You could also use DateTime and friends to avoid shelling out entirely.

Controlling arguments in perl with Getopt::Long

I am trying to use Getopt::Long add command line arguments to my script (seen below). The problem I am running into is related to multiple commands that do different things. For example I have an option flag that sets the configuration file to use with the script the option is -c [config_path] and I also have -h for help.
The problem I am running into is I need to have a condition that states whether or not the config option has been used AND a config file has been specified. I tried counting the options in #ARGV but found if -h and -c are specifed it causes the script to move on the to the subroutine load_config anyway. Because as seen in the code below when 2 arguments are found in #ARGV it fires the subroutine.
In what way could I fix this? At least in my head specifying -h and -c at the same time sorta contradicts each other. Is there a way to make it so only "informational commands" like help cannot be executed with "operational commands" like -c? Heck is there a way where I get a list of the commands that have been passed? I tried printing the contents of #ARGV but nothing was in it even though I had specified command arguments.
#!/usr/bin/perl
use strict;
use warnings;
use Getopt::Long;
use Term::ANSIColor;
use XML::Simple;
use Net::Ping;
use Net::OpenSSH;
use Data::Dumper;
# Create a new hash to copy XML::Simple configuration file data into
my %config_file;
# Clear the screen and diplay version information
system ("clear");
print "Solignis's Backup script v0.8 for ESX\\ESX(i) 4.0+\n";
print "Type -h or --help for options\n\n";
# Create a new XML::Simple object
my $xml_obj = XML::Simple->new();
# Create a new Net::Ping object
my $ping_obj = Net::Ping->new();
my $config_file;
my $argcnt = $#ARGV + 1;
GetOptions('h|help' => \&help,
'c|config=s' => \$config_file
);
if ($argcnt == 0) {
print "You must supply a config to be used\n";
} elsif ($argcnt == 2) {
if (! -e $config_file) {
print color 'red';
print "Configuration file not found!\n";
print color 'reset';
print "\n";
die "Script Halted\n";
} else {
load_config();
}
}
sub load_config {
print color 'green';
print "$config_file loaded\n";
print color 'reset';
my $xml_file = $xml_obj->XMLin("$config_file",
SuppressEmpty => 1);
foreach my $key (keys %$xml_file) {
$config_file{$key} = $xml_file->{$key};
}
print Dumper (\%config_file);
}
sub help {
print "Usage: backup.pl -c [config file]\n";
}
#ARGV is altered by GetOptions, that is why it seems empty. Rather than counting arguments, just directly check if $config_file is defined.
BTW, IMO there is no need to try to exclude -c from being used with -h. Normally a "help" just prints the help text and exits without taking any other action, check that first and it shouldn't matter whether -c is supplied or not.
Something like
my $help;
my $config_file;
GetOptions('h|help' => \$help,
'c|config=s' => \$config_file
);
if ( defined $help ) {
help();
} elsif ( defined $config_file ) {
...;
} else {
die "No arguments!";
}
You might also want to check out Getopt::Euclid which presents some expanded ways to provide options and a cool way of using the programs documentation as the spec for the command-line arguments.
You can always set a default value for the options eg my $help = 0; my $config_file = ""; and then test for those values.