in reply to Inconsistent behavior of Getopt::Std

parameter_check.pl -b -ddd produces non-intuitive result, where -ddd is eaten by -b and becomes the value of option -b.

This would be a mistake on the part of the user invoking the command. You've told the module that option -b takes a value, so the module is simply doing what you asked: taking the next value in @ARGV as the value of the option. Note that other command-line tools act the same way, e.g. grep -e -i causes -i to be taken as the value for the -e option. This does not look like a bug in the library to me.

parameter_check.pl -ddd -b the result is as expected: options('b') is set to zero length string.

This is actually an invalid command: option -b requires a value, and it's not getting one. You're not checking the return value of getopts, which would have told you something is wrong. A correct command line to specify the empty string for the option -b would have been parameter_check.pl -ddd -b ''.

the behavior of this module varies depending on if the parameter is the last in the line or not

Based on the above, this is not the correct conclusion to draw.

Not all command-line tools agree on a standard way of processing options, so if you want different behavior, you may have to use a different library or implement it yourself - though I wouldn't advise the latter, since you'd just be adding yet another differing way to process options to the mix.

During testing I discovered that this module correctly processes setting the value of an option via repetition of the option letter , like -ddd.

Not really, it's simply taking dd as the value of the -d option. Getopt::Long actually handles this as a separate case - otherwise, its behavior is identical to Getopt::Std for the following test cases, plus it gives a helpful error message.

use warnings; use strict; use Getopt::Std; # Note that removing posix_default and gnu_compat has no effect # on the output of this particular script. use Getopt::Long qw/ :config posix_default gnu_compat bundling /; use Data::Dump; my @tests = ( [qw/ -b -ddd /], [qw/ -b -c -ddd /], [qw/ -ddd -b /], [qw/ -ddd -c -b /], [qw/ -ddd -b -c /], [qw/ -ddd -c -b -- /], [qw/ -c -b -ddd /], [qw/ -b -c -ddd /], ); for my $t (@tests) { print "##### "; dd $t; { local @ARGV = @$t; if ( getopts("b:cd:", \my %opts) ) { dd \%opts } else { warn "Bad options\n" } } { local @ARGV = @$t; if ( GetOptions(\my %opts, 'b=s', 'c', 'd+') ) { dd \%opts } else { warn "Bad options\n" } } } __END__ ##### ["-b", "-ddd"] { b => "-ddd" } { b => "-ddd" } ##### ["-b", "-c", "-ddd"] { b => "-c", d => "dd" } { b => "-c", d => 3 } ##### ["-ddd", "-b"] Bad options Option b requires an argument Bad options ##### ["-ddd", "-c", "-b"] Bad options Option b requires an argument Bad options ##### ["-ddd", "-b", "-c"] { b => "-c", d => "dd" } { b => "-c", d => 3 } ##### ["-ddd", "-c", "-b", "--"] { b => "--", c => 1, d => "dd" } { b => "--", c => 1, d => 3 } ##### ["-c", "-b", "-ddd"] { b => "-ddd", c => 1 } { b => "-ddd", c => 1 } ##### ["-b", "-c", "-ddd"] { b => "-c", d => "dd" } { b => "-c", d => 3 }

Replies are listed 'Best First'.
Re^2: Inconsistent behavior of Getopt::Std
by likbez (Sexton) on Aug 16, 2020 at 18:29 UTC
    Not really, it's simply taking dd as the value of the -d option.
    True. The code is pretty convoluted and is also very old. But the key logic can easily be improved adding less then a dosen of lines. Legacy code like built in help screen via option -help that nobody needs can be removed. The value of @EXPORT array is unclear and probably can be removed too (archaeological Perl). All useful functionality can be implemented in 40 lines or less. Here is my variant, which probably can be considerably improved as this idea of extracting $first and $rest via regex is open to review: they split at the fixed position.
    #!/usr/bin/perl
    use v5.10;
       use warnings;
       use strict 'subs';
       use feature 'state';
       use Getopt::Std;
    
       getopts('b:cd:',\%options);
       foreach $opt (keys(%options)) {
          if($options{$opt} eq '' ){
             say "$opt is set to ''";
          }else{
             say "$opt is set to $options{$opt}";
          }         
       }
       exit 0;
    sub getopts
    {
    my ($argumentative,$hash)=@_;
    my (@args,$first,$rest,$pos);
       @args = split( //, $argumentative );
       while(@ARGV && ($_ = $ARGV[0]) =~ /^-(.)(.*)$/s ){
          ($first,$rest) = ($1,$2);
          if (/^--$/) {	# early exit if --
             shift @ARGV;
             last;
          }
          $pos = index($argumentative,$first);
          if( $pos==-1) {
             warn("Undefined option -$first skipped without processing\n");
             shift(@ARGV);
             next;
          }
          if (defined($args$pos+1) and ($args$pos+1 eq ':')) {
             # option with parameters
             if( $rest eq ''){          
                unless( @ARGV ){
                   warn("End of line reached for option -$first which requires argument\n");
                   $$hash{$first}='';
                   last;
               }
               if ( $ARGV[0] =~/^-/ ) {
                   warn("Option -$first requires argument\n");
                   $$hash{$first} = '';
               }else{
                   $$hash{$first}=$ARGV[0];
                   shift(@ARGV); # get next chunk
               }
             } else {
                if( ($first x length($rest)) eq $rest ){
                   $$hash{$first} = length($rest)+1;
                }else{
                   $$hash{$first}=$rest;
                }
                shift(@ARGV);
             }
          }else {
             $$hash{$first} = 1; # set the option
             if ($rest eq '') {
                shift(@ARGV);
             } else {
                $ARGV[0] = "-$rest"; # there can be other options without arguments after the first
             }
          }
       }
    }
    

    Here are two test runs:

    [0]  # perl  Std2.pl  -c -b -ddd
    Option -b requires argument
    d is set to 3
    c is set to 1
    b is set to ''
     
    [0]  # perl   Std2.pl  -c -ddd -b
    End of line reached for option -b which requires argument
    d is set to 3
    c is set to 1
    b is set to ''
    
      >True. The code is pretty convoluted and is also very old. But the key logic can easily be improved adding less then a dosen of lines. Legacy code like built in help screen via option -help that nobody needs can be removed.

      Moving the goal post?

      >archaeological Perl

      Do you mean vestigial?

      >is open to review

      Might want to take that to GH. I wouldn't call comments you get here a code review.