Как передать как обязательные, так и необязательные аргументы командной строки в сценарий perl?

Я использую Getopt::Long для передачи параметров моему Perl-скрипту.

Но я хочу сделать что-то вроде этого:

perl myScript mandatoryArgument1 -optionalArgument1=someValue

Я хочу, чтобы скрипт выдавал ошибку, если отсутствует requiredArgument1. Как это может быть сделано?


person user2524261    schedule 26.05.2016    source источник
comment
Вы можете найти эта статья полезная.   -  person jreisinger    schedule 26.05.2016


Ответы (2)


У хорошего Getopt::Long нет для этого механизма. Это конкретно обрабатывает параметры.

Однако, когда он выполняет свою работу, он удаляет эти параметры из @ARGV, поэтому после его завершения вы можете проверить, присутствуют ли ожидаемые аргументы. См. вторую часть для этого, но я хотел бы сначала предложить другой способ: сделать эти аргументы именованными, а затем Getopt обработает их.

Тогда легко проверить, были ли они отправлены. Например

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;
}

Поэтому, если --name string не указано в командной строке, $mandatoryArg остается неопределенным, и программа завершает работу. Эта переменная не нуждается в значении по умолчанию, поскольку она является обязательной, и она не должна иметь его, чтобы эта проверка работала.

Проверка и обработка аргументов часто требуют гораздо большего внимания, и именно здесь Getopt сияет.


mandatoryArgument1 в вопросе указывается без имени. В то время как Getopt можно заставить действовать на входе без опций, он не может определить, что ожидаемого нет.

Модуль позволяет смешивать аргументы с именованными параметрами в любом месте командной строки. См. Опция с другими аргументами в документах. Таким образом, вы можете вызвать программу как

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

но я бы предложил пользователю указать их после именованных опций.

Затем, после того, как GetOptions сделает свое дело, @ARGV будет содержать строку unnamed_arg и вы сможете ее получить (или узнать, что ее там нет). Обработка именованных опций с помощью GetOptions такая же, как описано выше.

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();
};

Выше вы должны обработать @ARGV вручную, как только Getopt подберет именованные аргументы.

Если имеется более одного такого аргумента, пользователь должен строго соблюдать их ожидаемое относительное положение в командной строке, поскольку в общем случае программа не может определить, что к чему. Так что ошибки, когда пользователь путает свой порядок в командной строке, вообще невозможно отловить.

Это становится помехой, и я бы предложил иметь максимум один тип безымянного аргумента(ов) и только в том случае, когда очевидно, что это должно быть, например, имя(я) файла(ов).

Хотя все это возможно, такие модули, как Getopt, существуют именно для того, чтобы нам не приходилось этого делать.


 Действие для ввода, которое не похоже на параметр, настроено с использованием имени '<>'

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

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

где подпрограмма arg_cb вызывается только в том случае, если просматривается неопциональный аргумент.

person zdim    schedule 26.05.2016

Мой подход к 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;

[...]

}

В этом случае опция --file или -f обязательна. Я проверяю, определено ли $file, в противном случае прерываю выполнение, выводя справку по программе.

Я бы не стал смешивать именованные входные параметры в форме --param=*value* с безымянными параметрами. Тем не менее, вы можете манипулировать @ARGV перед вызовом Getopt::Long и настроить его так, чтобы он делал то, что вы просили, но смешивание двух типов входных параметров философия для пользователей вашего скрипта сбивает с толку.

#!/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();

Вы можете назвать это как ./test.pl mandatory -f file -t tag:

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

Вам придется ограничить обязательный параметр (ы) фиксированной позицией (в моем примере первый, как в вопросе, но он также может быть последним).

person LaintalAy    schedule 26.05.2016