2009-05-20 9 views
2

je le code suivant dans mon script perl:Perl Getopt :: Longue connexes Question - Arguments de ligne de commande Mutually Exclusive

 

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

Quelle est la meilleure façon de gérer les arguments de ligne de commande mutuellement exclusifs? Dans mon script, je veux seulement que l'utilisateur entre seulement l'argument de ligne de commande "--dir" ou "--files" mais pas les deux. Est-il possible de configurer Getopt pour ce faire?

Merci.

+2

Ces undefs sont inutiles, les variables commencent avec la valeur undef (ou vides dans le cas de tableaux et de hachages). –

Répondre

4

Je ne pense pas qu'il y ait un moyen dans Getopt :: Long de le faire, mais il est assez facile à implémenter par vous-même (je suppose qu'il existe une fonction d'utilisation qui renvoie une chaîne qui indique à l'utilisateur appeler le programme):

die usage() if defined $directory and @files; 
2

Pourquoi ne pas simplement ceci:

if ($directory && @files) { 
    die "dir and files options are mutually exclusive\n"; 
} 
+1

Parce que 0 est un nom de répertoire valide et "0" est faux. –

2

Vous pouvez simplement vérifier l'existence de valeurs dans les deux variables.

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

Ou, si vous souhaitez simplement ignorer toutes les options spécifiées après la première --dir ou --files, vous pouvez pointer à la fois à une fonction.

#!/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

Que faire si le répertoire est la chaîne "0"? Vous devez vérifier si oui ou non il n'est pas défini ou non. –

+0

Ah, oui. Mon erreur. –

+0

Où est le mode $ supposé être défini? –

0
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 
0

Vous pouvez le faire avec Getopt::Long::Descriptive. C'est un peu différent de Getopt::Long, mais si vous imprimez un résumé d'utilisation, cela aide à réduire la duplication en faisant tout cela pour vous.

Ici, j'ai ajouté une option cachée appelée source, donc $opt->source qui contiendra la valeur dir ou files selon l'option a été donnée, et il appliquera la contrainte one_of pour vous. Les valeurs données seront en $opt->dir ou $opt->files, selon celle qui a été donnée.

my ($opt, $usage) = describe_options(
    '%c %o', 
    [ "source" => hidden => { 
     'one_of' => [ 
      [ "dir=s" => "Directory" ], 
      [ "[email protected]" => "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})); 
} 

La principale différence pour le reste de votre script est que toutes les options sont contenues comme méthodes de la variable $opt, plutôt que chacun ayant sa propre variable comme avec Getopt::Long.

Questions connexes