Unknown options in perl Getopt::Long - perl

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]

Related

perl - use command-line argument multiple times

I'm modifying a perl script in which the command line arguments are parsed like this:
if ($arg eq "-var1") {
$main::variable1 = shift(#arguments)
} elsif ($arg eq "-var2") {
$main::variable2 = shift(#arguments)
} elsif ($arg eq "var3") {
$main::variable3 = shift(#arguments)
} ...
So there is a whole bunch of elsif statements to cover all command-line arguments.
I'm now in a situaton where I want to use the argument '-var2' multiple times.
So my main::variable2 should maybe be an array that contains all values that are passed with "-var2".
I found that with Perl::getopt, this can be easily achieved (Perl Getopt Using Same Option Multiple Times).
However the way that my script parses its command-line arguments is different. So I was wondering if it could be achieved, without having to change the way the arguments are parsed.
That's not your actual code, is it? It won't even compile.
I'd be really surprised if Getopt::Long can't solve your problem and it's really a better idea to use a library rather than writing your own code.
But changing your code to store -var2 options in an array is simple enough.
my ($variable1, #variable2, $variable3);
if ($arg eq "-var1") {
$variable1 = shift(#arguments)
} elsif ($arg eq "-var2") {
push #variable2, shift(#arguments)
} elsif ($arg eq "-var3") {
$variable3 = shift(#arguments)
}
(I've also removed the main:: from your variables and added the, presumably missing, $s. It's really unlikely that you want to be using package variables rather than lexical variables.)
This particular wheel already exists. Please don't try to reinvent it. That just makes it a pain for the people trying to use your script. There's no reason to force people to learn a whole new set of rules in order to execute your program.
use File::Basename qw( basename );
use Getopt::Long qw( );
my $foo;
my #bars;
my $baz;
sub help {
my $prog = basename($0);
print
"Usage:
$prog [options]
$prog --help
Options:
--foo foo
...
--bar bar
May be used multiple times.
...
--baz baz
...
";
exit(0);
}
sub usage {
if (#_) {
my ($msg) = #_;
chomp($msg);
say STDERR $msg;
}
my $prog = basename($0);
say STDERR "Try '$prog --help' for more information.";
exit(1);
}
sub parse_args {
Getopt::Long::Configure(qw( posix_default ));
Getopt::Long::GetOptions(
"help" => \&help,
"foo=s" => \$foo,
"bar=s" => \#bars,
"baz=s" => \$baz,
)
or usage();
!#ARGV
or usage("Too many arguments");
return #ARGV;
}
main(parse_args());
Well, it is good practice to document your core -- you would appreciate it as soon as you return to make changes
NOTE: in Linux it requires perl-doc package to be installed to use --man option in full extent
#!/usr/bin/perl
#
# Description:
# Describe purpose of the program
#
# Parameters:
# Describe parameters purpose
#
# Date: Tue Nov 29 1:18:00 UTC 2019
#
use warnings;
use strict;
use Getopt::Long qw(GetOptions);
use Pod::Usage;
my %opt;
GetOptions(
'input|i=s' => \$opt{input},
'output|o=s' => \$opt{output},
'debug|d' => \$opt{debug},
'help|?' => \$opt{help},
'man' => \$opt{man}
) or pod2usage(2);
pod2usage(1) if $opt{help};
pod2usage(-exitval => 0, -verbose => 2) if $opt{man};
print Dumper(\%opt) if $opt{debug};
__END__
=head1 NAME
program - describe program's functionality
=head1 SYNOPSIS
program.pl [options]
Options:
-i,--input input filename
-o,--output output filename
-d,--debug output debug information
-?,--help brief help message
--man full documentation
=head1 OPTIONS
=over 4
=item B<-i,--input>
Input filename
=item B<-o,--output>
Output filename
=item B<-d,--debug>
Print debug information.
=item B<-?,--help>
Print a brief help message and exits.
=item B<--man>
Prints the manual page and exits.
=back
B<This program> accepts B<several parameters> and operates with B<them> to produce some B<result>
=cut

How to use GetOptions to detect trailing strings?

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

How to pass directory path as arguments from command line using perl?

My question as follows:
I struck with how to pass the command line arguments instead of passing directory path using perl .
Example suppose if am executing the file as follows:
./welcome.pl -output_dir "/home/data/output"
My code:
#!/usr/local/bin/perl
use strict;
use warnings 'all';
use Getopt::Long 'GetOptions';
GetOptions(
'output=s' => \my $output_dir,
);
my $location_dir="/home/data/output";
print $location_dir;
Code explanation:
I tried to print the contents in the $output_dir.so i need to pass the command line arguments inside the variable (i.e $location_dir) instead of passing path directly how can i do it?
use strict;
use warnings 'all';
use File::Basename qw( basename );
use Getopt::Long qw( GetOptions );
sub usage {
if (#_) {
my ($msg) = #_;
chomp($msg);
print(STDERR "$msg\n");
}
my $prog = basename($0);
print(STDERR "$prog --help for usage\n");
exit(1);
}
sub help {
my $prog = basename($0);
print(STDERR "$prog [options] --output output_dir\n");
print(STDERR "$prog --help\n");
exit(0);
}
Getopt::Long::Configure(qw( posix_default ));  # Optional, but makes the argument-handling consistent with other programs.
GetOptions(
'help|h|?' => \&help,
'output=s' => \my $location_dir,
)
or usage();
defined($location_dir)
or usage("--output option is required\n");
print("$location_dir\n");
Or course, if the argument is indeed required, then why not just use ./welcome.pl "/home/data/output" instead of an not-really optional parameter.
use strict;
use warnings 'all';
use File::Basename qw( basename );
use Getopt::Long qw( GetOptions );
sub usage {
if (#_) {
my ($msg) = #_;
chomp($msg);
print(STDERR "$msg\n");
}
my $prog = basename($0);
print(STDERR "$prog --help for usage\n");
exit(1);
}
sub help {
my $prog = basename($0);
print(STDERR "$prog [options] [--] output_dir\n");
print(STDERR "$prog --help\n");
exit(0);
}
Getopt::Long::Configure(qw( posix_default ));  # Optional, but makes the argument-handling consistent with other programs.
GetOptions(
'help|h|?' => \&help,
)
or usage();
#ARGV == 1
or usage("Incorrect number of arguments\n");
my ($location_dir) = #ARGV;
print("$location_dir\n");

GetOptions Check Option Values

I am updating an existing Perl script that uses GetOptions from Getopt::Long. I want to add an option that takes a string as its parameter and can only have one of 3 values: small, medium, or large. Is there any way to make Perl throw an error or kill the script if any other string value is specified? So far I have:
my $value = 'small';
GetOptions('size=s' => \$value);
You could use a subroutine to handle the processing of that option.
User-defined subroutines to handle options
my $size = 'small'; # default
GetOptions('size=s' => \&size);
print "$size\n";
sub size {
my %sizes = (
small => 1,
medium => 1,
large => 1
);
if (! exists $sizes{$_[1]}) {
# die "$_[1] is not a valid size\n";
# Changing it to use an exit statement works as expected
print "$_[1] is not a valid size\n";
exit;
}
$size = $_[1];
}
I put the sizes into a hash, but you could use an array and grep as toolic showed.
One way is to use grep to check if the value is legal:
use warnings;
use strict;
use Getopt::Long;
my $value = 'small';
GetOptions('size=s' => \$value);
my #legals = qw(small medium large);
die "Error: must specify one of #legals" unless grep { $_ eq $value } #legals;
print "$value\n";
It's just one of a few checks you need to perform after GetOptions returned.
You need to check if GetOptions succeeded.
You may need to check the value provided for each optional argument.
You may need to check the number of arguments in #ARGV.
You may need to check the arguments in #ARGV.
Here's how I perform those checks:
use Getopt::Long qw( );
my %sizes = map { $_ => 1 } qw( small medium large );
my $opt_size;
sub parse_args {
Getopt::Long::Configure(qw( :posix_default ));
$opt_size = undef;
Getopt::Long::GetOptions(
'help|h|?' => \&exit_with_usage,
'size=s' => \$opt_size,
)
or exit_bad_usage();
exit_bad_usage("Invalid size.\n")
if defined($size) && !$sizes{$size};
exit_bad_usage("Invalid number of arguments.\n")
if #ARGV;
}
Here's how I handle failures:
use File::Basename qw( basename );
sub exit_with_usage {
my $prog = basename($0);
print("usage: $prog [options]\n");
print(" $prog --help\n");
print("\n");
print("Options:");
print(" --size {small|medium|large}\n");
print(" Controls the size of ...\n"
exit(0);
}
sub exit_bad_usage {
my $prog = basename($0);
warn(#_) if #_;
die("Use $prog --help for help\n");
exit(1);
}
This might be overkill, but also take a look Getopt::Again, which implements validation through its process configuration value per command line argument.
use strict;
use warnings;
use Getopt::Again;
opt_add my_opt => (
type => 'string',
default => 'small',
process => qr/^(?:small|medium|large)$/,
description => "My option ...",
);
my (%opts, #args) = opt_parse(#ARGV);
An alternative to Getopt::Long is Getopt::Declare which has built in pattern support, but is slightly more verbose:
use strict;
use warnings;
use feature qw/say/;
use Getopt::Declare;
my $args = Getopt::Declare->new(
join "\n",
'[strict]',
"-size <s:/small|medium|large/>\t small, medium, or large [required]"
) or exit(1);
say $args->{-size};
Test runs:
[hmcmillen]$ perl test.pl -size small
small
[hmcmillen]$ perl test.pl -size medium
medium
[hmcmillen]$ perl test.pl -size large
large
[hmcmillen]$ perl test.pl -size extra-large
Error: incorrect specification of '-size' parameter
Error: required parameter -size not found.
Error: unrecognizable argument ('extra-large')
(try 'test.pl -help' for more information)

Getting -h with Pod::Usage and GetOpt::Long violates of the DRY principle

I would like to give a better documentation to my Perl Programs. For this I don't have many solutions. Actuatlly I found only one: the POD.
For the NAME section we usually have:
=head1 NAME
program_name - Short description
=cut
It takes 5 lines where the only relevant information is the short description. The program_name should be automatically filled with basename($0).
Then it comes the options. From the GetOptions arguments, I can automatically extract:
Name of each option
Is the argument mandatory
We can easily add options such as category, mandatory, description and in_conflict_with. So why should I repeat myself in the POD?
To illustrate my example I wrote this:
#!/usr/bin/env perl
use strict;
use warnings;
use feature 'say';
use Getopt::Long;
use Pod::Usage 'pod2usage';
use File::Basename;
=head1 AUTHOR
John Doe <j#doe.com>
=head1 DESCRIPTION
B<This program> does nothing important. But at least it is properly documented.
=head1 COPYRIGHT
Copyright(c) Nobody
=head1 VERSION
v0.1
=cut
my %cfg;
CliInfo (
args => [
{arg => "l|length=i", dest => \$cfg{length}, desc => "Length of the string", type => 'mandatory'},
{arg => "f|file=s" , dest => \$cfg{data}, desc => "Input filename", type => 'mandatory'},
{arg => "v" , dest => sub {\$cfg{verbose}++}, desc => "Verbose"},
]
);
sub CliInfo {
my %info = #_;
my $programName = basename $0;
GetOptions(map( {$_->{arg} => $_->{dest}} #{$info{args}}),
'h|help' => sub {
say "Usage: $programName [options]...";
say pod2scalar(-verbose => 99, -sections => "DESCRIPTION");
say "\nOptions:";
say sprintf('%-20s', (sub {
my #opt = split /\|/, shift =~ s/=.*//r;
return " -$opt[0], --$opt[1]" if #opt > 1;
return " --$opt[0]" if length $opt[0] > 1;
return " -$opt[0]";
})->($_->{arg})), $_->{desc} for #{$info{args}};
say "\n";
},
'version' => sub {
my $ver = pod2scalar(-verbose => 99, -sections => "VERSION");
say STDERR $programName, " - $ver";
say pod2scalar(-verbose => 99, -sections => "COPYRIGHT");
say "";
exit;
})
or say "Invalid option, try --help" and exit 1;
sub pod2scalar {
open my $fh, '>', \my $text;
pod2usage(#_, -output => $fh, -exitval => 'NOEXIT');
$text =~ s/^(?:.*\n)//;
$text =~ s/(?:\s*\n)\z//r;
}
}
__END__
Which gives this output (Also pretty compatible with the GNU standards):
$ ./help.pl --help
Usage: help.pl [options]...
This program does nothing important. But at least it is properly documented.
Options:
-l, --length Length of the string
-f, --file Input filename
-v Verbose
So the question:
Is there any standard and similar solution to what I am showing here with respect of the DRY principle?
Maybe Perl library Getopt::Long::Descriptive is what you need.
This is your script rewritten with Getopt::Long::Descriptive:
#!/usr/bin/env perl
use strict;
use warnings;
use Pod::Usage 'pod2usage';
use Capture::Tiny ':all';
use Getopt::Long::Descriptive;
=head1 AUTHOR
John Doe <j#doe.com>
=head1 DESCRIPTION
B<This program> does nothing important. But at least it is properly documented.
=head1 COPYRIGHT
Copyright(c) Nobody
=head1 VERSION
v0.1
=cut
sub pod2scalar {
my $stdout = capture_merged {
pod2usage(-verbose => 99, -sections => "DESCRIPTION", -exitval => "noexit");
};
return $stdout;
}
my ($opt, $usage) = describe_options(
pod2scalar() . "%c %o <some-arg>",
[ 'l|length=i', 'Length of the string', { required => 1, default => 4 } ],
[ 'f|file=s', 'Input filename', { required => 1 } ],
[ 'v', 'Verbose' ],
[ 'help', "print usage message and exit" ],
{
show_defaults => 1,
},
);
if ($opt->help) {
print($usage->text);
exit;
}
This is the output of your original script:
$ ./before.pl --help
Usage: before.pl [options]...
This program does nothing important. But at least it is properly
documented.
Options:
-l, --length Length of the string
-f, --file Input filename
-v Verbose
And here is the output of the new script:
$ ./after.pl --help
Mandatory parameter 'f' missing in call to (eval)
Description:
This program does nothing important. But at least it is properly
documented.
after.pl [-flv] [long options...] <some-arg>
--length INT -l INT Length of the string
(default value: 4)
--file STR -f STR Input filename
-v Verbose
--help print usage message and exit