Perl Getopt :: Длинный связанный вопрос - взаимоисключающие аргументы командной строки - PullRequest
4 голосов
/ 20 мая 2009

В моем скрипте perl есть следующий код:


my $directory;
my @files;
my $help;
my $man;
my $verbose; 

undef $directory;
undef @files;
undef $help;
undef $man;
undef $verbose;

GetOptions(
           "dir=s" => \$directory,  # optional variable with default value (false)
           "files=s" => \@files,    # optional variable that allows comma-separated
                                # list of file names as well as multiple 
                    # occurrenceces of this option.
           "help|?" => \$help,      # optional variable with default value (false)
           "man" => \$man,          # optional variable with default value (false)
           "verbose" => \$verbose   # optional variable with default value (false)
          );

    if (@files) {
    @files = split(/,/,join(',', @files));
    }

Каков наилучший способ обработки взаимоисключающих аргументов командной строки? В моем скрипте я хочу, чтобы пользователь вводил только аргумент командной строки "--dir" или "--files", но не оба одновременно. Есть ли способ настроить Getopt для этого?

Спасибо.

Ответы [ 5 ]

4 голосов
/ 20 мая 2009

Я не думаю, что в Getopt :: Long есть способ сделать это, но его достаточно легко реализовать самостоятельно (я предполагаю, что есть функция use, которая возвращает строку, которая сообщает пользователю, как вызвать программу):

die usage() if defined $directory and @files;
3 голосов
/ 20 мая 2009

Почему бы не просто так:

if ($directory && @files) {
  die "dir and files options are mutually exclusive\n";
}
2 голосов
/ 20 мая 2009

Вы можете просто проверить наличие значений в обеих переменных.

if(@files && defined $directory) {
    print STDERR "You must use either --dir or --files, but not both.\n";
    exit 1;
}

Или, если вы хотите просто игнорировать любые параметры, указанные после первых ключей --dir или --files, вы можете указать обе функции.

#!/usr/bin/perl

use Getopt::Long;

my $directory;
my @files;
my $mode;
my $help;
my $man;
my $verbose; 

GetOptions(
    "dir=s" => \&entries,    # optional variable with default value (false)
    "files=s" => \&entries,  # optional variable that allows comma-separated
                             # list of file names as well as multiple 
                             # occurrences of this option.
    "help|?" => \$help,      # optional variable with default value (false)
    "man" => \$man,          # optional variable with default value (false)
    "verbose" => \$verbose   # optional variable with default value (false)
);

sub entries {

   my($option, $value) = @_;

    if(defined $mode && $mode ne $option) {
        print STDERR "Ignoring \"--$option $value\" because --$mode already specified...\n";
    }
    else {
        $mode = $option unless(defined $mode);
        if($mode eq "dir") {
            $directory = $value;
        }
        elsif($mode eq "files") {
            push @files, split(/,/, $value);
        }
    }

    return;

}

print "Working on directory $directory...\n" if($mode eq "dir");
print "Working on files:\n" . join("\n", @files) . "\n" if($mode eq "files");
0 голосов
/ 08 сентября 2016

Вы можете сделать это с помощью Getopt::Long::Descriptive. Это немного отличается от Getopt::Long, но если вы печатаете сводку об использовании, это помогает уменьшить дублирование, делая все это за вас.

Здесь я добавил скрытую опцию с именем source, поэтому $opt->source, которая будет содержать значение dir или files в зависимости от того, какая опция была задана, и будет применять ограничение one_of вы. Указанные значения будут в $opt->dir или $opt->files, в зависимости от того, какое из них было дано.

my ( $opt, $usage ) = describe_options(
    '%c %o',
    [ "source" => hidden => {
        'one_of' => [
            [ "dir=s" => "Directory" ],
            [ "files=s@" => "FilesComma-separated list of files" ],
        ]
    } ],
    [ "man" => "..." ],          # optional variable with default value (false)
    [ "verbose" => "Provide more output" ],   # optional variable with default value (false)
    [],
    [ 'help|?' => "Print usage message and exit" ],
);
print( $usage->text ), exit if ( $opt->help );

if ($opt->files) {
    @files = split(/,/,join(',', @{$opt->files}));
}

Основным отличием остальной части вашего сценария является то, что все параметры содержатся в качестве методов переменной $opt, а не в каждой, имеющей собственную переменную, как в Getopt::Long.

0 голосов
/ 21 мая 2009
use strict;
use warnings;
use Getopt::Long;

my($directory,@files,$help,$man,$verbose);

GetOptions(
  'dir=s'   => sub {
    my($sub_name,$str) = @_;
    $directory = $str;

    die "Specify only --dir or --files" if @files;
  },

  # optional variable that allows comma-separated
  # list of file names as well as multiple 
  # occurrences of this option.
  'files=s' => sub {
    my($sub_name,$str) = @_;
    my @s = split ',', $str;
    push @files, @s;

    die "Specify only --dir or --files" if $directory;
  },    

  "help|?"  => \$help,
  "man"     => \$man,
  "verbose" => \$verbose,
);

use Pod::Usage;
pod2usage(1) if $help;
pod2usage(-exitstatus => 0, -verbose => 2) if $man;
=head1 NAME

sample - Using Getopt::Long and Pod::Usage

=head1 SYNOPSIS

sample [options] [file ...]

 Options:
   -help            brief help message
   -man             full documentation

=head1 OPTIONS

=over 8

=item B

Print a brief help message and exits.

=item B

Prints the manual page and exits.

=back

=head1 DESCRIPTION

B will read the given input file(s) and do something
useful with the contents thereof.

=cut
...