use Getopt::Long qw(GetOptionsFromArray); use Test::More; my %getopt_spec = ( 'first=s{2}' => 1, 'second=i{3}' => 2, 'third' => 3, ); my @testARGV = ( ['--second', '1', '2', '3', '--first', 'ahah', 'and spaces', '--third'], ['--first', 'ahah', 'and spaces', '--second', '1', '2', '3'], ['--first', 'ahah', 'and spaces', '--third'], ['--third', '--first', 'ahah', 'and spaces', '--second', '1', '2', '3'], ['--third'], ['--first', 'ahah', 'and spaces'], ['--second', '1', '2', '3'], [] ); my @expectedARGV = ( ['--first', 'ahah', 'and spaces', '--second', '1', '2', '3', '--third'], ['--first', 'ahah', 'and spaces', '--second', '1', '2', '3'], ['--first', 'ahah', 'and spaces', '--third'], ['--first', 'ahah', 'and spaces', '--second', '1', '2', '3', '--third'], ['--third'], ['--first', 'ahah', 'and spaces'], ['--second', '1', '2', '3'], [] ); for my $i (0..@testARGV-1){ my $testar = $testARGV[$i]; my $expear = $expectedARGV[$i]; # make a copy of it for printing diags because getopt_in_order() will destroy it my $copy_testar = [ @$testar ]; my $newargv = getopt_in_order(\%getopt_spec, $testar); ok(defined $newargv, "getopt_in_order() called for '@$copy_testar'."); is_deeply($newargv, $expear, "got (@$newargv) and expected (@$expear) for '@$copy_testar'"); } done_testing; # returns the ordered @$an_argv as per the @$options_order # or undef on failure # WARNING: $an_argv will be destroyed on return sub getopt_in_order { # by bliako for https://perlmonks.org/?node_id=11140961 # 01/Feb/2022 my ( # a hashref keyed on getopt specs $getopt_spec, # a hash of option names with the index you want them processed, e.g. 'first' => 1 #$options_order, # arrayref to @ARGV or its copy, this will be destroyed by Getopt $an_argv ) = @_; my %options_order = map { (split('=', $_))[0] => $getopt_spec->{$_}-1 } keys %$getopt_spec; my %getopt_spec; my @tmpARGV; for my $aspec (keys %$getopt_spec){ $getopt_spec{$aspec} = sub { my $k = shift; my $expects_args = @_ && ($aspec =~ /^.+?=.+?$/); my $idx = $options_order{$k}; if( exists($tmpARGV[$idx]) && defined($tmpARGV[$idx]) ){ push @{$tmpARGV[$idx]}, @_ if $expects_args } else { if( $expects_args ){ $tmpARGV[$idx] = [ '--'.$k, @_ ] } else { $tmpARGV[$idx] = [ '--'.$k ] } } } } if( ! GetOptionsFromArray($an_argv, %getopt_spec) ){ print STDERR "getopt_in_order() : error parsing command line arguments.\n"; return undef } # remove undef entries e.g. because -second was not present # see https://stackoverflow.com/a/11123138 @tmpARGV = grep defined, @tmpARGV; my @newARGV; foreach my $opt (@tmpARGV){ # in correct order now and no holes push @newARGV, @$opt } return \@newARGV; }