views:

1262

answers:

4

Hi,

I have the following code in my perl script:


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

What is the best way to handle mutually exclusive command line arguments? In my script I only want the user to enter only the "--dir" or "--files" command line argument but not both. Is there anyway to configure Getopt to do this?

Thanks.

+3  A: 

I don't think there is a way in Getopt::Long to do that, but it is easy enough to implement on your own (I am assuming there is a usage function that returns a string that tells the user how to call the program):

die usage() if defined $directory and @files;
Chas. Owens
+1  A: 

Why not just this:

if ($directory && @files) {
  die "dir and files options are mutually exclusive\n";
}
Alex
Because 0 is a valid directory name and "0" is false.
Chas. Owens
+1  A: 

You can simply check for the existence of values in both variables.

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

Or, if you would like to simply ignore any options specified after the first --dir or --files, you can point both at a function.

#!/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");
Kendrick Erickson
What if directory is the string "0"? You need to check whether or not it is defined not whether or not it is true.
Chas. Owens
Ah, yep. My mistake.
Kendrick Erickson
Where is $mode supposed to be defined?
Dr. Faust
"You need to check whether or not it is defined not whether or not it is true." - which line(s) are you referring to?
Dr. Faust
Kendrick Erickson
When I ran my script with the entries subroutine I got an error message telling me that $mode was not defined.
Dr. Faust
A: 
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
Brad Gilbert