Perl Getopt::Long Assigning variable then going to subroutine - perl

I have the following piece of code
my $use = "Use: " . basename($0) . " [options]";
my $version = "Version: 0.1 \n";
my $variableA;
my $variableB;
GetOptions(
'a=s' => \$variableA,
'help' => sub { print $use; exit 0 },
'version' => sub { print $version; exit 0 },
'b' => sub { \$variableB, &this_subroutine; goto NOWGOHERE; },
);
die "Incorrect use. \n" unless (defined $variableA || defined $variableB);
sub this_subroutine {
print "$variableB\n";
}
NOWGOHERE: print "HELLO I'M NOW HERE\n";
What I am trying to do is set $variableB and then do the &this_subroutine and the goto NOWGOHERE but I can only get it to do one or the other, not both, using either 'b=s' => \$variableB, or sub { &this_subroutine; goto NOWGOHERE;0 },
When trying to do both I cannot seem to print the $variableB, is there something obvious I am missing or doing wrong syntactically?
Using 'b=s' => \$variableB, sub { &this_subroutine; goto NOWGOHERE; }, does not seem to work either?
your help is much appreciated, many thanks

$variableB will never have a value because you never assign to it.
'a=s' => \$variableA,
gives $variableA a value because, when Getopt::Long is given a scalar ref, it assigns the option's value to that scalar.
On the other hand,
'b' => sub { \$variableB, &this_subroutine; goto NOWGOHERE; },
gives Getopt::Long a code reference, which it can't assign the option value to.
Based on the docs, it appears that it passes the option name and option value to the coderef as parameters, in which case
'b=s' => sub { $variableB = $_[1]; this_subroutine(); goto NOWGOHERE; },
should probably do what you want.

Related

parse all arguments and store to hash

How can i parse all the arbitrary arguments to a hash without specifying the argument names inside my perl script.
Running command with below argument should give hash like below.
-arg1=first --arg2=second -arg3 -arg4=2.0013 -arg5=100
{
'arg2' => 'second',
'arg1' => 'first',
'arg4' => '2.0013',
'arg3' => 1,
'arg5' => 100
};
This can be achieved using Getopt::Long as below
GetOptions(\%hash,
"arg1=s",
"arg2=s",
"arg3",
"arg4=f",
"arg5=i");
However, my argument list is too long and i don't want to specify argument names in GetOptions.
So a call to GetOptions with only hash as a parameter should figure out what arguments are (and their type integer/string/floats/lone arguments) and just create a hash.
There are a lot of Getopt modules. The following are some that will just slurp everything into a hash like you desire:
Getopt::Mini
Getopt::Whatever
Getopt::Casual
I personally would never do something like this though, and have no real world experience with any of these modules. I'd always aim to validate every script for both error checking and as a means to self-document what the script is doing and uses.
Try this:
#!/usr/bin/perl
use warnings;
use strict;
use Data::Dumper;
sub getOptions {
my (%opts, #args);
while (#_) {
my $opt = shift;
if ($opt =~ /^-/) {
if ($opt =~ /-+([^=]+)(?:=(.+))?/) {
$opts{$1} = $2 ? $2 : 1;
}
}
else {
push #args, $opt;
}
}
return (\%opts, \#args);
}
my ($opts, $args) = getOptions(#ARGV);
print Dumper($opts, $args);
Testing:
$ perl t.pl -arg1=first --arg2=second -arg3 -arg4=2.0013 -arg5=100 datafile
$VAR1 = {
'arg2' => 'second',
'arg1' => 'first',
'arg4' => '2.0013',
'arg3' => 1,
'arg5' => '100'
};
$VAR2 = [
'datafile'
];
This will work as expected for your example,
my %hash = map { s/^-+//; /=/ ? split(/=/, $_, 2) : ($_ =>1) } #ARGV;

Getopt::Long and anonymous subroutine

I've written the following code:
my $version = sub {
print "$PROGNAME $VERSION - $AUTHOR\n";
exit 0;
};
my $usage = sub {
print "Usage: proll <options>\n";
print "Available options:\n";
print " -h, --help Print this help and exit.\n";
print " --version Print version.\n";
print " XdY Launch X dice with Y faces.\n";
exit 0;
};
my $ret = GetOptions ( "version" => \$version,
"h|help" => \$usage );
But also if I call the script with --version or --help it doesn't call the subroutine. Where am I wrong?
And if I change the code as follows, it always call the first subroutine also without any command line parameter:
my $ret = GetOptions ( "version" => &$version,
"h|help" => &$usage );
\$version is a reference to $version, where $version is a reference to an anonymous subroutine; so, \$version is a reference to a reference to a subroutine. That's too much indirection. You just need a single level of reference-ness:
my $ret = GetOptions ( "version" => $version,
"h|help" => $usage );

Perl Getopt::Long Related Question - Mutually Exclusive Command-Line Arguments

I have the following code in my perl script:
my $directory;
my #files;
my $help;
my $man;
my $verbose;
undef $directory;
undef #files;
undef $help;
undef $man;
undef $verbose;
GetOptions(
"dir=s" => \$directory, # optional variable with default value (false)
"files=s" => \#files, # optional variable that allows comma-separated
# list of file names as well as multiple
# occurrenceces of this option.
"help|?" => \$help, # optional variable with default value (false)
"man" => \$man, # optional variable with default value (false)
"verbose" => \$verbose # optional variable with default value (false)
);
if (#files) {
#files = split(/,/,join(',', #files));
}
What is the best way to handle mutually exclusive command line arguments? In my script I only want the user to enter only the "--dir" or "--files" command line argument but not both. Is there anyway to configure Getopt to do this?
Thanks.
I don't think there is a way in Getopt::Long to do that, but it is easy enough to implement on your own (I am assuming there is a usage function that returns a string that tells the user how to call the program):
die usage() if defined $directory and #files;
Why not just this:
if ($directory && #files) {
die "dir and files options are mutually exclusive\n";
}
You can simply check for the existence of values in both variables.
if(#files && defined $directory) {
print STDERR "You must use either --dir or --files, but not both.\n";
exit 1;
}
Or, if you would like to simply ignore any options specified after the first --dir or --files, you can point both at a function.
#!/usr/bin/perl
use Getopt::Long;
my $directory;
my #files;
my $mode;
my $help;
my $man;
my $verbose;
GetOptions(
"dir=s" => \&entries, # optional variable with default value (false)
"files=s" => \&entries, # optional variable that allows comma-separated
# list of file names as well as multiple
# occurrences of this option.
"help|?" => \$help, # optional variable with default value (false)
"man" => \$man, # optional variable with default value (false)
"verbose" => \$verbose # optional variable with default value (false)
);
sub entries {
my($option, $value) = #_;
if(defined $mode && $mode ne $option) {
print STDERR "Ignoring \"--$option $value\" because --$mode already specified...\n";
}
else {
$mode = $option unless(defined $mode);
if($mode eq "dir") {
$directory = $value;
}
elsif($mode eq "files") {
push #files, split(/,/, $value);
}
}
return;
}
print "Working on directory $directory...\n" if($mode eq "dir");
print "Working on files:\n" . join("\n", #files) . "\n" if($mode eq "files");
You can do this with Getopt::Long::Descriptive. It's a bit different from Getopt::Long, but if you're printing a usage summary, it helps to reduce duplication by doing all that for you.
Here, I've added a hidden option called source, so $opt->source which will contain the value dir or files depending on which option was given, and it will enforce the one_of constraint for you. The values given will be in $opt->dir or $opt->files, whichever one was given.
my ( $opt, $usage ) = describe_options(
'%c %o',
[ "source" => hidden => {
'one_of' => [
[ "dir=s" => "Directory" ],
[ "files=s#" => "FilesComma-separated list of files" ],
]
} ],
[ "man" => "..." ], # optional variable with default value (false)
[ "verbose" => "Provide more output" ], # optional variable with default value (false)
[],
[ 'help|?' => "Print usage message and exit" ],
);
print( $usage->text ), exit if ( $opt->help );
if ($opt->files) {
#files = split(/,/,join(',', #{$opt->files}));
}
The main difference for the rest of your script is that all the options are contained as methods of the $opt variable, rather than each one having its own variable like with Getopt::Long.
use strict;
use warnings;
use Getopt::Long;
my($directory,#files,$help,$man,$verbose);
GetOptions(
'dir=s' => sub {
my($sub_name,$str) = #_;
$directory = $str;
die "Specify only --dir or --files" if #files;
},
# optional variable that allows comma-separated
# list of file names as well as multiple
# occurrences of this option.
'files=s' => sub {
my($sub_name,$str) = #_;
my #s = split ',', $str;
push #files, #s;
die "Specify only --dir or --files" if $directory;
},
"help|?" => \$help,
"man" => \$man,
"verbose" => \$verbose,
);
use Pod::Usage;
pod2usage(1) if $help;
pod2usage(-exitstatus => 0, -verbose => 2) if $man;
=head1 NAME
sample - Using Getopt::Long and Pod::Usage
=head1 SYNOPSIS
sample [options] [file ...]
Options:
-help brief help message
-man full documentation
=head1 OPTIONS
=over 8
=item B
Print a brief help message and exits.
=item B
Prints the manual page and exits.
=back
=head1 DESCRIPTION
B will read the given input file(s) and do something
useful with the contents thereof.
=cut

Setting default value with Params::Validate if an undefined value is passed in

I am having trouble getting Params::Validate to work how I want it to.
#!/usr/local/bin/perl -w
use strict;
use Params::Validate qw/:all/;
use Data::Dumper;
sub foo {
my %args = validate(#_, {
bar => {
default => 99,
# TODO - Somehow use a callback to return a default if undefined
# ??!!??
# callbacks => { call_me => sub { return 99 if !defined shift }},
},
});
# $args{bar} //= 99; # Don't want to define default in 2 places
print Dumper(\%args);
}
foo({ bar => undef });
So how do I set / test for an undef in the args list and replace with the said 'default' value with Params::Validate ??
You need to be setting $_[0]:
call_me => sub { $_[0] = 99 if not defined $_[0] }
#_ is aliased to the parameters passed in, so you can use this as a reference to the original.
Also,
$args{bar} ||= 99;
would reset bar even if it were 0 or '' (empty string), which doesn't sound like what you want. Using
$args{bar} //= 99;
if you're using perl 5.10 or later would do what you want, though.
Based on your comment of not duplicating the default, the best I could come up with is:
sub foo
{
unshift #_, bar => undef;
my %args = validate(#_,
{
bar => {
optional => 1,
callbacks => {
call_me => sub { $_[0] = 99 unless defined $_[0] },
},
},
},
);
print Dumper \%args;
}
That seems to work, but I have to admit - it's ugly.

Is it possible to define anonymous subroutines in a hash constructor in Perl?

Is it possible to define anonymous subroutines in a hash constructor in Perl?
I'm trying to do something like this:
my %array = { one => sub { print "first $_[0]" },
two => sub { print "next $_[0]" },
three => sub { print "last $_[0]" }};
$array{$foo}->('thing');
But it isn't working. The code seems to run and compile, but the values in the array are blank. If I do this:
my %array;
$array{'one'} = sub { print "first $_[0]" };
$array{'two'} = sub { print "next $_[0]" };
$array{'three'} = sub { print "last $_[0]" };
$array{$foo}->('thing');
Then it seems to work fine. So I have a workaround, but it's just bugging me and I wondered if anyone knows whether it's possible and, if so, what the syntax is.
It looks like you're assigning into the hash incorrectly. Using {} constructs an anonymous hash reference, which you assign to a scalar. But you're assigning to a named hash (%array).
You need to assign into a scalar:
my $array = { one => sub { print "first $_[0]" },
two => sub { print "next $_[0]" },
three => sub { print "last $_[0]" }};
$array->{$foo}->('thing');
Or to not use the anon constructor syntax:
my %array = ( one => sub { print "first $_[0]" },
two => sub { print "next $_[0]" },
three => sub { print "last $_[0]" });
$array{$foo}->('thing');
That's because in the first case, you're creating a hashref not a hash, what you want is:
my $array;
$array = { one => ... }; # not %array = { .. };
...
$array->{one}->('thing');
Greg Hewgill is on the right track. Always enable the strict pragma. Also, always enable warnings--but I recommend against using the -w switch in your code.
Instead, take advantage of the use warnings pragma. That way you can selectively disable particular groups of warnings in lexically scoped areas of your code.
For example:
use strict;
use warnings;
foo('bar');
foo();
sub foo {
no warnings 'uninitialized';
my $foo = shift || 'DEFAULT';
print "Foo is $foo\n";
}
Consistent use of the strict and warnings pragmas will save you hours and hours of time.
I spent something like two hours trying to track down this exact braces-vs-parentheses problem in a script not too long ago. If you use the -w switch to Perl, then you'll get a warning "Reference found where even-sized list expected", which at least gives a clue where to look. Today, all Perl scripts should start with:
#!/usr/bin/perl -w
use strict;
Your Perl life will be immeasurably less frustrating.
It's supposed to be parenthesis, not curly braces:
my %array = ( one => sub { print "first $_[0]" },
two => sub { print "next $_[0]" },
three => sub { print "last $_[0]" }});
'{ a => 1, b => 2 }' produces a reference to a hash. So the following would also work:
my $array = { one => sub { print "first $_[0]" },
two => sub { print "next $_[0]" },
three => sub { print "last $_[0]" }};
$array->{one}->('thing');