How to pass both mandatory and optional command line arguments to perl script?

蓝咒 提交于 2019-12-06 12:13:07

问题


I am using Getopt::Long to pass options to my Perl script.

But I want to do something like this :

perl myScript mandatoryArgument1 -optionalArgument1=someValue

I want the script to throw an error if mandatoryArgument1 is missing. How can this be done?


回答1:


The good Getopt::Long does not have a mechanism for that. It specifically processes options.

However, as it does its work it removes those options from @ARGV so once it's finished you can check whether the expected arguments are there. See the second part for this but I would like to first suggest another way: Make those arguments named and then Getopt will process them.

Then it is easy to check whether they were submitted. For example

use warnings;
use strict;
use feature 'say';
use Getopt::Long;

my $mandatoryArg;
my $opt;

# Read command-line arguments, exit with usage message in case of error
GetOptions( 'name=s' => \$mandatoryArg, 'flag' => \$opt )
    or usage(); 

if (not defined $mandatoryArg) {
    say STDERR "Argument 'name' is mandatory";
    usage();
}

# The program goes now. Value for $opt may or may have not been supplied

sub usage {
    say STDERR "Usage: $0 ...";   # full usage message
    exit;
}

So if --name string isn't given on the command line the $mandatoryArg stays undefined and the program exits. That variable doesn't need a default value since it is mandatory, and it shouldn't have one for this check to work.

Argument checking and processing is often far more involved, and this is when Getopt shines.


The mandatoryArgument1 in the question is supplied without a name. While Getopt can be made to act on a non-option input, it cannot detect that an expected one is not there.

The module does allow to mix arguments with named options, anywhere on the command line. See Option with other arguments in docs. So you can invoke the program as

script.pl --opt1 value1 unnamed_arg --opt2 value2

but I'd suggest to the user to supply them after named options.

Then, after GetOptions does its job, @ARGV will contain the string unnamed_arg and you can get it (or find out that it isn't there). Processing of named options by GetOptions is the same as above.

my ($var1, $var2, $flag);

GetOptions('opt1=s' => \$var1, 'opt2=i' => \$var2, 'f' => \$flag)
    or usage(); 

# All supplied named options have been collected, all else left in @ARGV
# Read the remaining argument(s) from @ARGV, or exit with message

# This can get far more complicated if more than one is expected
my $mandatoryArg1 = shift @ARGV || do {
    say STDERR "Mandatory argument (description) is missing";
    usage();
};

Above you have to process @ARGV by hand once Getopt picked up the named arguments. If there are more than one such argument you have to strictly respect their relative position on the command line, and it may be hard to check whether the user may have confused their order.

While all that is possible modules like Getopt exist precisely so that we don't have to do it.


  Action for input that doesn't look like an option is set up using the "name" of '<>'

Getoptions( 'opt=s' => \$var, ..., '<>' => \&arg_cb );

sub arg_cb { say "Doesn't look like an option: $_[0]" }

where the sub arg_cb is invoked only if a non-option-looking argument is seen.




回答2:


My approach with Getopt::Long:

sub help { print "Some help"; exit }

sub main {
    GetOptions(
        'file|f=s'   => \( my $file = undef ),
        'tag|t=s'    => \( my $tag = undef ),
        'help|h'     => \( my $printHelp = undef ),
    );

    help() if $printHelp;
    help() unless $file;

[...]

}

In this case, the option --file or -f is mandatory. I check if $file is defined, otherwise I abort execution printing the help of the program.

I wouldn't mix named input parameters in the form --param=*value* with no-named parameters. That said, you can manipulate @ARGV before calling Getopt::Long and configure it to do what you have asked, but it is confusing to mix two types of input parameter philosophy for the users of your script.

#!/usr/bin/env perl
use strict;
use warnings;
use feature qw{say};
use Carp;
use Getopt::Long;    

sub main {    
    my $firstParam = shift @ARGV;
    croak "Mandatory parameter not given"
        if (!$firstParam || $firstParam =~ /^-/);

    GetOptions(
        'file|f=s'   => \( my $file = undef ),
        'tag|t=s'    => \( my $tag = undef ),
    );


    say 'Mandatory: ', $firstParam;
    say 'Optional $file: ', $file if $file;
    say 'Optional $tag: ', $tag if $tag;
}

main();

You can call it as ./test.pl mandatory -f file -t tag:

Mandatory: mandatory                                                                                        
Optional $file: file                                                                                      
Optional $tag: tag  

You will have to restrict the mandatory parameter(s) to a fixed position (in my example the first as in the question, but it could be the last ones also).



来源:https://stackoverflow.com/questions/37453445/how-to-pass-both-mandatory-and-optional-command-line-arguments-to-perl-script

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!