4

In Perl's Getopt::Long version 2.39 I could use

use Getopt::Long qw( :config gnu_getopt );
GetOptions(
   \my %opts, 
   "codon-view|c:20",    # Optional value, default 20
   "consensus|C:50", 
   ...
)

to indicate that if I use -c the default value would be 20 put in %optsunder key codon-view when -c is given but no explicit value for it is there. On the other hand -c or --codon-view is not supplied, then no value in the hash table is stored for in %opts.

In 2.48 this no longer works and I don't see in Getopt::Long's documentation

$ perl -E'
   use Getopt::Long qw( :config gnu_getopt );
   say $Getopt::Long::VERSION;
   GetOptions(\my %opts, "codon-view|c:20");
   say $opts{"codon-view"} // "[undef]"
' -- -c
2.39
20

$ perl -E'
   use Getopt::Long qw( :config gnu_getopt );
   say $Getopt::Long::VERSION;
   GetOptions(\my %opts, "codon-view|c:20");
   say $opts{"codon-view"} // "[undef]"
' -- -c
2.48
[undef]

How can I achieve the old behavior?

Help!

rocky
  • 7,226
  • 3
  • 33
  • 74
  • Are you sure that worked in 2.39? Going all the way back to 2.24 (released in 2000), the [documentation for options with values](https://metacpan.org/pod/release/JV/Getopt-Long-2.24/lib/Getopt/Long.pm#Options-with-values) consistently shows option specs of the form `tag=s` or `tag:i`, not `tag:20`. – ThisSuitIsBlackNot Jun 01 '16 at 22:01
  • I've modified the post to include this. What you get matches my behavior. It is only when `-c` or `--codon-view` is given without a value that the behavior changes betwee 2.39 and 2.48. Also there are changes in FindOption that change between those two versions with respect to `gnu_getopt`. – rocky Jun 02 '16 at 01:17
  • 1
    @ThisSuitIsBlackNot, Search for `: number` – ikegami Jun 02 '16 at 03:54
  • 1
    This is a change introduced in 2.48. I'm not sure, but I think it was done unintentionally, so I filed a [bug report](https://rt.cpan.org/Ticket/Display.html?id=114999). – ikegami Jun 02 '16 at 03:59

4 Answers4

5

This is a change introduced in 2.48.

$ perl -E'
   use Getopt::Long qw( :config gnu_getopt );
   say $Getopt::Long::VERSION;
   GetOptions(\my %opts, "codon-view|c:20");
   say $opts{"codon-view"} // "[undef]"
' -- -c
2.47
20

$ perl -E'
   use Getopt::Long qw( :config gnu_getopt );
   say $Getopt::Long::VERSION;
   GetOptions(\my %opts, "codon-view|c:20");
   say $opts{"codon-view"} // "[undef]"
' -- -c
2.48
[undef]

I'm not sure, but I think it was done unintentionally, so I filed a bug report.


use Getopt::Long qw( :config gnu_getopt );

is short for

use Getopt::Long qw( :config gnu_compat bundling permute no_getopt_compat );

How invested are you in using gnu_compat?

$ perl -E'
   use Getopt::Long qw( :config gnu_getopt );
   say $Getopt::Long::VERSION;
   GetOptions(\my %opts, "codon-view|c:20");
   say $opts{"codon-view"} // "[undef]"
' -- -c
2.48
[undef]

$ perl -E'
   use Getopt::Long qw( :config gnu_compat bundling permute no_getopt_compat );
   say $Getopt::Long::VERSION;
   GetOptions(\my %opts, "codon-view|c:20");
   say $opts{"codon-view"} // "[undef]"
' -- -c
2.48
[undef]

$ perl -E'
   use Getopt::Long qw( :config bundling permute no_getopt_compat );
   say $Getopt::Long::VERSION;
   GetOptions(\my %opts, "codon-view|c:20");
   say $opts{"codon-view"} // "[undef]"
' -- -c
2.48
20

gnu_compat controls whether --opt= is allowed, and what it should do. Without gnu_compat, --opt= gives an error. With gnu_compat, --opt= will give option opt and empty value. This is the way GNU getopt_long() does it.

So if you're ok with --codon-view= assigning zero to $opts{"codon-view"}, simply use

use Getopt::Long qw( :config bundling permute no_getopt_compat );

instead of

use Getopt::Long qw( :config gnu_getopt );
ThisSuitIsBlackNot
  • 23,492
  • 9
  • 63
  • 110
ikegami
  • 367,544
  • 15
  • 269
  • 518
  • Since this is a usability thing, I'll need to ask users to experiment. What I've done for now is use the 2.39 code copied into the module. Given what you say, I suppose I can increase the version up to 2.47 or so. – rocky Jun 02 '16 at 08:44
  • Look again. My solution won't affect any of your users. It simply makes `--opt=` stop throwing an error. – ikegami Jun 02 '16 at 11:09
  • It looks like the documentation for `gnu_compat` is wrong. `perl -MGetopt::Long=:config,gnu_compat -E'GetOptions(\%opts, "foo:1") or die; say "<$opts{foo}>"' -- --foo=` prints `<0>`; removing `gnu_compat` prints `<1>` (instead of giving an error). – ThisSuitIsBlackNot Jun 03 '16 at 17:11
  • @ThisSuitIsBlackNot, It does die without `gnu_compat` for `=` options. /// Getopt::Long's test suite is woefully lacking. – ikegami Jun 03 '16 at 17:17
  • @ikegami just checked. Yep this works as you say. And this should be fine. Thanks. – rocky Jun 03 '16 at 20:56
3

Set the default value before the GetOptions() call. If the option is not provided on the command line, then the default value will not be overwritten.

$ perl -MGetopt::Long -E '$c=20;GetOptions("c=i"=>\$c); say $c' -- -c 14
14

$ perl -MGetopt::Long -E '$c=20;GetOptions("c=i"=>\$c); say $c' --
20

There is a trivial example in the Getopt::Long documentation.

mob
  • 117,087
  • 18
  • 149
  • 283
  • I guess I wasn't clear about the previous behavior. If `-c` is not given then then that key isn't in a hash. If it is set, then and only then does the value for that key have a default value. I'll edit my question to reflect this. – rocky Jun 02 '16 at 01:06
1

Here is another possible but less good solution: include a copy of Getopt::Long.pm, It's only one file, but I have changed the package namespace to something different, e.g. MyPackage::GetoptLong.

This is not an ideal answer, but it is something to keep in mind if you need something to keep compatibility and don't have the better ikegami solution.

rocky
  • 7,226
  • 3
  • 33
  • 74
0

I like to assign my opts to a hash..

GetOptions(\ my %opt,
    'codon-view|c:i',
);

if ( exists $opt{'codon-view'} ) {
    print "User triggered '-c' flag\n";
    $opt{'codon-view'} ||= 20;
    printf( "codon-view: %d\n", $opt{'codon-view'} );
}

Now if a user runs ./you-app -c with no argument, the $opt{c} key gets created, but it's value is undef, so you much check if it was triggered with exists.

The ||= operator assigns the right-hand side to the left-hand side only if the left-hand side is falsey ( usually undef ). A caveat is that if someone does -c 0 it will assign the default... but I'm going to go ahead and assume 0 is probably a bad argument for your flag.

elcaro
  • 2,227
  • 13
  • 15
  • I'm sorry I didn't report my situation fully. First, yes, I do use an options hash. Also, I use `use Getopt::Long qw(config gnu_getopt);` Your example does work when gnu_getopt is not specified in the use statement. However when it is specified, then your example no longer works. Also it is a little more cumbersome and puts in two places what `codon-view|c:20` does. If we can can address the problem when gnu_getopt is specified, I'll accept this if no better solution is found. – rocky Jun 02 '16 at 01:33