I do not subscribe to your logic. It's best to first parse all arguments and have a flag that all checks have passed, and then act on each argument. Just like haukex in Re: Getopt - Validate arguments before processing (updated) suggests. It's cumbersome but from what I understand you are asking for the impossible: parse the arguments as they are entered by the user, do some destructive actions (e.g. --erase-all-files) in your first() et al. and then realise that the last argument does not validate and you need to restore original state. How can you rollback that? Only if you create the actions and you only commit them when all validates, eventually.

Perhaps you can compromise with having Getopt::Long processing your options in a specific and strict order, independently on how the user types them on the command line. In this way, you can achieve having only one "destructive" option at the end. In my code, the getopt_in_order() will take an @ARGV, the Getopt spec and the order you want the options to be processed and return you back a new @ARGV which will be in that order. You then simply call Getopt::Long::GetOptionsFromArray(\@newARGV, %getopt_spec).

I am sure this will be of little help to your problem but hey, I always wanted such feature from Getopt::Long.

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', '--th +ird'], ['--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', '--th +ird'], ['--first', 'ahah', 'and spaces', '--second', '1', '2', '3'], ['--first', 'ahah', 'and spaces', '--third'], ['--first', 'ahah', 'and spaces', '--second', '1', '2', '3', '--th +ird'], ['--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() w +ill 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 (@$expe +ar) 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 processe +d, e.g. 'first' => 1 #$options_order, # arrayref to @ARGV or its copy, this will be destroyed by Get +opt $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 a +rguments.\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; }

bw, bliako


In reply to Re: Getopt - Validate arguments before processing by bliako
in thread Getopt - Validate arguments before processing by g_speran

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.