Script does not die if there are incorrect arguments - perl

I'm quite new to the Perl programming. I want to use GetOptions to parse input arguments for my script. I have simple problem - I want script to exit with usage message if there's mess in arguments or arguments are missing values (for mandatory arguments).
src_xml is mandatory argument but if I run the script not providing value for this argument, I get an error message about usage of uninitialized value $src_xml in -e at tenant_tenant.pl. What am I doing wrong? Or do I have to check every variable if its defined?
my $dev;
my $src_xml;
my $tgt_syscd = 'L86';
my $tgt_path = '/tmp/test/exports';
my $help;
GetOptions('src_xml=s' => \$src_xml,
'tgt_syscd=s' => \$tgt_syscd,
'tgt_path=s' => \$tgt_path,
'dev' => \$dev,
'h|help' => \$help
) or die "Usage: perl $0 --src_xml NAME --tgt_syscd NAME --tgt_path NAME
\n";
#checking for help
if ( defined $help ) {
die $help_message;
}

Your script will die only this way:
perl ./foo.pl --src_xml=
Check arguments with custom subroutine?
GetOptions(
'dec-to-base35=i' => \&dec_to_base35,
'base35-to-dec=s' => \&base35_to_dec,
);
sub dec_to_base35 ( $opt_name, $decimal_value ) {
...
Complete example here
BTW, you can join "Perl weekly challenge", this may help to learn something new

Related

how to pass command line arguments to getopt in perl

I am new to Perl. I have to pass command line arguments to GetOptions method so I can access those variables. I have tried the following code:
use Getopt::Long;
$result = GetOptions($ARGV[0] => \$serv_name, $ARGV[1] => \$serv_id);
print "Server name is $serv_name & server id is $serv_id";
But I am not getting desired output. So how do I do this?
Try this (inspired from documentation of Getopt::Long):
use Getopt::Long;
GetOptions(
"server_id=i" => \my $serv_id, # numeric
"server_name=s" => \my $serv_name, # string
) or die "Error in command line arguments\n";
print "Server name is $serv_name & server id is $serv_id\n";
If server_id argument maybe not numeric, change "server_id=i" to "server_id=s"
And call the program these ways:
%> perl ex.pl --server_name=the_name --server_id=1234
# or
%> perl ex.pl -server_name the_name -server_id 1234

combine perl GetoptLong with special single ARGV case?

I am translating a shell script to a Perl script that uses Getopt::Long, and I want to keep compatibility with the case below where, if the only argument to the script is a single file, that file is used as a config file, whereas the usual is to get parameters into GetoptLong.
if [[ $# -eq 1 && -f $1 ]];
then
echo "Using config file $1"
[...]
else
if [ $# -lt 2 ]; then usage "INCORRECT NUMBER OF PARAMETERS"; fi
while getopts ":a:b:c:d:ef" opt;
do
[...]
One option is to maintain the if/else in the Perl script like so:
if (1 == #ARGV && -f $ARGV[0]) {
# use this config file
config_file_method($ARGV[0]);
} else {
# use GetOptions
GetOptions(
'a|foo:s' => \$foo,
'b|bar:s' => \bar,
[...]
);
}
But I wonder if this special case could be included in the GetOptions
function with some magic:
GetOptions(
'if only one element in #ARGV' => 'call config_file_method($ARGV[0])',
'a|foo:s' => \$foo,
'b|bar:s' => \bar,
[...]
);
Any ideas?
I don't see anything in the Getopt::Long documentation which supports what you are looking for.
The approach I would take is to let GetOptions process #ARGV. If anything is still in #ARGV, then you can assume it is the config file. Then there is no need for the -f check because the config_file_method sub will do an open/die check anyway.
GetOptions(
'a|foo:s' => \$foo,
'b|bar:s' => \$bar,
);
config_file_method($ARGV[0]) if #ARGV;
I'm pretty sure that it's not possible. If you was passing the config_file as an option instead of an argument you could do something like that:
GetOptions(
'c|config_file' => sub { config_file_method($ARGV[0]) if 1 == scalar #ARGV } ,
'a|foo:s' => \$foo,
'b|bar:s' => \bar,
[...]
);
There is no such option in Getopt::Long.
And as you've demonstrated, there's absolutely no need for one. (It didn't save you any work, yet it added complexity by inventing a new sublanguage.)

How to quote this parameter in perl module

I'm trying to use the AnyEvent::Twitter::Stream module and I want to reference a file that lists the Twitter uids I want to follow. I can put the uids in the code itself and it works as follows:
my $done = AnyEvent->condvar;
my $nt_filter = AnyEvent::Twitter::Stream->new(
username => $cf{account},
password => $cf{password},
method => 'filter',
follow => '15855509,14760150,18598536',
on_tweet => sub {
#some code.....
},
on_error => sub {
my $error = shift;
debug "ERROR: $error";
},
timeout => 45,
);
$done->recv;
But when I try to do the same using a file as such:
my $done = AnyEvent->condvar;
my $nt_filter = AnyEvent::Twitter::Stream->new(
open UID_FILE, "/tmp/uids" or die $!;
my #uid_line = <UID_FILE>;
username => $cf{account},
password => $cf{password},
method => 'filter',
follow => #uid_file,
on_tweet => sub {
#some code....
},
on_error => sub {
my $error = shift;
debug "ERROR: $error";
},
timeout => 45,
);
$done->recv;
it fails. The uids file has the following contents:
'15855509,14760150,18598536'
I'm getting a 406 error from Twitter, suggesting the format is not correct. I'm guessing the quotes are not correct somehow?
The AnyEvent::Twitter::Stream module subclasses AnyEvent and you don't need to access the base module at all. All the functionality is provided by the new method and the callbacks that you specify there, and you shouldn't call AnyEvent->condvar or $done->recv.
The open call and assignment to #uid_line don't belong inside the call to AnyEvent::Twitter::Stream->new.
Furthermore the variable you are using to supply the value for the follow parameter is #uid_file instead of #uid_line.
You must use strict; and use warnings; at the start of your programs, especially if you are asking for help with them. This will trap simple mistakes like this that you could otherwise overlook.
You can't in general use an array to supply a single scalar value. In this instance it may be OK as long as the file has only a single line (so there is only one element in the array) but there is a lot that could go wrong.
In addition you are passing single-quotes in the value that don't belong there: they appear in the code only to mark the start and end of the Perl string.
I suggest you read all decimal strings from your file like this
open my $uid_fh, '<', '/tmp/uids' or die $!;
my #uids;
push #uids, /\d+/g for <$uid_fh>;
(Note that these lines belong before the call to AnyEvent::Twitter::Stream->new)
Then you can supply the follow parameter by writing
follow => join(',', #uids),
I hope this is clear. Please ask again if you need further help.
Edit
These changes incorporated into your code should look like this, but it is incomplete and I cannot guarantee that it will work properly.
use strict;
use warnings;
use AnyEvent::Twitter::Stream;
open my $uid_fh, '<', '/tmp/uids' or die $!;
my #uids;
push #uids, /\d+/g for <$uid_fh>;
my %cf = (
account => 'myaccount',
password => 'password',
);
my $nt_filter = AnyEvent::Twitter::Stream->new(
username => $cf{account},
password => $cf{password},
method => 'filter',
follow => join(',', #uids),
on_tweet => sub {
#some code....
},
on_error => sub {
my $error = shift;
debug "ERROR: $error";
},
timeout => 45,
);
It looks to me like you are trying to pass an array in a scalar context so it is possible that you are setting follow to 1, the number of elements in the array (the number of lines in the file).
Assuming there is only a single line of IDs in your file, does the following work:
open UID_FILE, "/tmp/uids" or die $!;
# Load the first line of uids
my $uid_line = <UID_FILE>;
# Remove apostrophes from input.
# This may or may not be necessary
$uid_line =~ s/'//g;
# Don't forget to close the file
close(UID_FILE);
my $done = AnyEvent->condvar;
my $nt_filter = AnyEvent::Twitter::Stream->new(
username => $cf{account},
password => $cf{password},
method => 'filter',
follow => $uid_line,
on_tweet => sub {
#some code....
},
on_error => sub {
my $error = shift;
debug "ERROR: $error";
},
timeout => 45,
);
$done->recv;
You will probably need to strip the leading and trailing apostrophes from your input line. For example:
$uid_line =~ s/^'//;
$uid_line =~ s/'$//;
You could probably get away with just removing all apostrophes, e.g.:
$uid_line =~ s/'//g;

How to use GetOptions utility to handle 'optional' command-line arguments in Perl?

There are many Perl tutorials explaining how to use GetOptions utility to process only the command-line arguments which are expected, else exit with an appropriate message.
In my requirement I have following optional command-line arguments, like,
-z zip_dir_path : zip the output
-h : show help.
I tried few combinations with GetOptions which did not work for me.
So my question is: How to use GetOptions to handle this requirement?
EDIT: -z needs 'zip directory path'
EDIT2:
My script has following compulsory command-line arguments:
-in input_dir_path : Input directory
-out output_dir_path : Output directory
Here's my code:
my %args;
GetOptions(\%args,
"in=s",
"out=s"
) or die &usage();
die "Missing -in!" unless $args{in};
die "Missing -out!" unless $args{out};
Hope this EDIT adds more clarity.
A : (colon) can be used to indicate optional options:
#!/usr/bin/env perl
use strict;
use warnings;
use Getopt::Long;
my ( $zip, $help, $input_dir, $output_dir );
GetOptions(
'z:s' => \$zip,
'h' => \$help,
'in=s' => \$input_dir,
'out=s' => \$output_dir,
);
From the documentation:
: type [ desttype ]
Like "=", but designates the argument as optional. If omitted, an
empty string will be assigned to string values options, and the
value zero to numeric options.
If you specify that and check for the empty string, you know which ones the user did not specify.
This should set to 1 or 0 the values of $zip_output and $show_help based on what input arguments you get in command line.
use strict;
use warnings;
use Getopt::Long;
my $zip_output;
my $show_help;
GetOptions("z" => \$zip, "h" => \$show_help);

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.