KurtSchwind has asked for the wisdom of the Perl Monks concerning the following question:
I'm a big fan of using Getopt in my command line programs. What I've become a little dissatisfied with the organization of the flags and variables. I'm looking for some input here from the monks on organizational tips. I'll post a sample of the code style I normally use.
#!/usr/bin/perl
use strict;
use Getopt::Long;
my $showos=0;
my $showrel=0;
my $showarch=0;
my $showiface=0;
my $showhd=0;
my $showmem=0;
my $showuser=0;
GetOptions (
"os|o"=>\$showos,
"rel|r"=>\$showrel,
"arch|a"=>\$showarch,
"iface|i"=>\$showiface,
"hd|h"=>\$showhd,
"mem|m"=>\$showmem,
"user|u"=>\$showuser,
"all|A"=> sub { $showos = 1; $showrel = 1; $showarch = 1; $sho
+wiface = 1; $showhd = 1; $showmem = 1; $showuser = 1; },
"help|h" => sub { &help; exit 0; }
);
. . .
Maybe this is already the best pattern to use, but it just looks kind of ugly to me. Any ideas would be appreciated.
--
“For the Present is the point at which time touches eternity.” - CS Lewis
Re: GetOpt Organization (or stop)
by tye (Sage) on Mar 09, 2015 at 14:33 UTC
|
Leaving the organization question to other respondents, you (and at least one respondent) made a common mistake. GetOptions() can report 1 or more errors via warn() and then return a false value. Because there can be more than 1 error, it doesn't use die(). So it is your responsibility to stop execution for such cases.
GetOptions(
# ...
) or exit 1;
Or use a die() as shown in Getopt::Long.
| [reply] [d/l] |
Re: GetOpt Organization
by hdb (Monsignor) on Mar 09, 2015 at 14:06 UTC
|
Not used this before myself, no idea whether there is an established pattern. Here is my proposal:
use strict;
use warnings;
use Getopt::Long;
my %parms = (
"os|o" =>\(my $showos =0),
"rel|r" =>\(my $showrel =0),
"arch|a" =>\(my $showarch =0),
"iface|i"=>\(my $showiface=0),
"hd|h" =>\(my $showhd =0),
"mem|m" =>\(my $showmem =0),
"user|u" =>\(my $showuser =0),
);
GetOptions (
%parms,
"all|A" => sub { $$_=1 for values %parms },
"help|h" => sub { &help; exit 0; }
);
$showos=5;
print "$showos,$showrel,$showarch,$showiface,$showhd,$showmem,$showuse
+r\n";
| [reply] [d/l] |
|
| [reply] [d/l] |
|
That may be so but the code seems to work anyway.
UPDATE: ...reason being that there are several ways to use GetOpt::Long, one of which does not require a hash ref as first parameter.
| [reply] |
|
Re: GetOpt Organization
by karlgoethebier (Abbot) on Mar 09, 2015 at 14:52 UTC
|
use Getopt::Long;
use Pod::Usage; # IMHO a must
Getopt::Long::Configure("no_ignore_case"); # double your options ;-)
GetOptions(\%options, "help", "foo=s","bar=s",); # or what ever
pod2usage( -exitstatus => 0, -verbose => 2 ) if $options{help};
foreach my $option (%options ) {
pod2usage( -exitstatus => 2, -verbose => 1 ) unless $option;
}
__END__
# POD section here
Something like this is also very handy (should be self explaining):
GetOptions(
\%options, "help", "suffix:s", "amount=i", "dir=s", "logpath=s", "
+From=s", "Subject:s", "Data:s", "Host:s",
"patterns=s{1,}" => \@patterns,
"To=s{1,}" => \@recipients
);
# stuff
__END__
./foo.pl -a 2 \
-d /path/to/dir \
-l /path/to/logs \
-F user@domain
-s suffix \
-p foo bar \
-T user@domain other@domain
Hint: If you declare "foo", you can call it as -f as well as --foo.
Regards, Karl
«The Crux of the Biscuit is the Apostrophe»
| [reply] [d/l] [select] |
|
tsk, tsk.. no monk checks for the return status of GetOptions ?? You want your program running with invalid option passed?
Here is my usual pattern:
use Getopt::Long;
use Pod::Usage;
unless ( GetOptions (
"color=s"=>\$par_color,
"help" => \$par_help,
)
) {pod2usage(-verbose => 1,-exitval => 'NOEXIT'); &wait_for_in
+put; exit;}
if (defined $par_help){pod2usage(-verbose => 1,-exitval => 'NOEXIT');
+&wait_for_input; exit;}
The function pod2usage supports -exitval => 'NOEXIT' (ie normally it exits the program unless you specify it to not do) useful if you started the program without a console, like in a Tk application as Tk Tartaglia's triangle fun - Pascal's triangle fun.
HtH L*
There are no rules, there are no thumbs..
Reinvent the wheel, then learn The Wheel; may be one day you reinvent one of THE WHEELS.
| [reply] [d/l] [select] |
|
tsk, tsk.. no monk checks for the return status of GetOptions ?? You want your program running with invalid option passed? The validation options and return value of GetOptions , and esp their combination, isn't that useful You still have to do your own validation, so let GetOptions warn, then write your program
| [reply] |
|
|
monks>options.pl --goo
Unknown option: goo
Best regards, Karl
«The Crux of the Biscuit is the Apostrophe»
| [reply] [d/l] |
|
|
|
Re: GetOpt Organization
by atcroft (Abbot) on Mar 09, 2015 at 14:03 UTC
|
Personally, I try to organize in a way that makes sense and is relatively easy to read. To me, that would mean putting the simple options in some order (either by type or, more likely for me, in alphabetical order), then putting more complex items (such as your "all" option) lower (also ordered), with the "help" option (if present) at the end. (I would also probably have the help() sub close by, so it would be easy to remember to update if an option were added/removed/changed.) I might also consider storing those configuration options in a hash, so as to make passing them easier.
Hope that helps, and I look forward to the responses you get on this thread for the possibility of ideas to consider as well.
| [reply] |
Re: GetOpt Organization
by choroba (Cardinal) on Mar 09, 2015 at 14:20 UTC
|
If the number of options is higher (i.e. more than 4, YMMV), I'd use a hash:
my %opt;
GetOtions(\%opt, qw( os|o rel|r arch|a iface|i hd|h mem|m ));
See "Storing options values in a hash" in Getopt::Long for details.
| [reply] [d/l] |
|
Whenever I touch Getopt::Long-using code that uses a hash, I usually change it to something that uses variables. In many of those cases, there are actually places in the code using an option name that isn't one of the ones populated by the call. "use strict" prevents such mistakes when variables are used.
I also prefer to reduce the scope of many of the options. Instead of making the hash available "everywhere", I can often make it clearer which options impact the behavior of a particular sub. Or that some option(s) impact nothing other than some small scope.
| [reply] |
|
"I also prefer to reduce the scope of many of the options. Instead of making the hash available "everywhere", I can often make it clearer which options impact the behavior of a particular sub. Or that some option(s) impact nothing other than some small scope."
Fascinating.
But to be honest, i'm a bit unsure if i guessed right what you mean.
Or in other words: IMHO there is nothing wrong with this idiom:
MAIN: {
my %options;
GetOptions(\%options, "foo", "bar");
nose ($options{foo}, $options{bar});
}
sub nose {
my ($foo, $bar) = @_;
# stuff
my $cuke;
}
N.B.: First time post of untested code.
Thank you very much for advice and best regards, Karl
«The Crux of the Biscuit is the Apostrophe»
| [reply] [d/l] |
|
|
|
|
Excuse my slow wit, but I can't see how a lexical scalar is less "everywhere" than a lexical hash? How do you reduce the scope of options?
| [reply] |
|
|