helpermethod
helpermethod

Reputation: 62224

Easy way to check for valid command line parameters?

I'm looking for an easy way to check for a correct number of command line parameters, displaying a usage message if an error occurs and then immediately exit.

I thought of something like

if (@ARGV < 3) {
  print STDERR "Usage: $0 PATTERN [FILE...]\n";
  exit 1;
}

Is this a valid pattern?

Upvotes: 15

Views: 41871

Answers (6)

U. Windl
U. Windl

Reputation: 4391

Actually it depends on your level of distrust (you did not specify what parameters are actually expected):

  • @ARGV < 3 just checks whether there are less than three parameters
  • What about extra parameters?
  • What about the type (or syntax) of each parameter?
  • What about the semantics of each parameter (e.g. a file that must exist)?

As suggested in https://stackoverflow.com/a/5022789/6607497 using Getopt::Long is a first step, but even arg1=s will allow the string to be anything, even empty.

As said above, you can extend Getopt::Long with your own checking functions (that would exceed the typical size of an answer, specifically when considering the very short (and little detailed) question), but a pattern could look like this:

# parse options
sub get_options()
{
    my $result = 1;
    my %options = ('angular-offset=f' => \$angular_offset,
                   'destination-point=s' => check_pair_param(\@p_dest),
                   'iteration-count=i' => check_positive_param(\$count),
                   'speed=f' => check_speed_param(\$speed),
                   'starting-point=s' => check_pair_param(\@p_start),
                   'translation-vector=s' => check_pair_param(\@v_transl),
                   'verbosity=i' => \$verbosity);

    use Getopt::Long;

    if (!GetOptions(%options))
    {
        print STDERR "valid options for $0 are:\n";
        foreach my $opt (sort(keys %options)) {
#...
        }
        print STDERR "\n";
        undef $result;
    }
    return $result;
}

In the example check_positive_param will check whether the parameter is a positive number (GetOptions and =i ensures that it's an integer already), maybe like this (warning: closures ahead):

# check for positive number
sub check_positive_param($)
{
    my $tgt_ref = shift;

    sub (;$$) {
        my ($name, $value) = @_;

        unless (defined($value)) {      # printable representation
            return $tgt_ref;
        } elsif ($value && $value > 0) {
            $$tgt_ref = $value;
        } else {
            $name = '--' . $name;
            die "$value: not a positive value for $name!\n";
        }
    }
}

The closure also allows to print the value of the associated option variable, and the reason for using a closure is that you can check different option values that all need to be positive.

For example specifying --it=-4 would result in an error message like this:

-4: not a positive value for --iteration-count!
valid options for ../homing.pl are:
...

Similar checks could be written to check for existing files, readable files, valid host names, etc.

For repeating options where values are collected in arrays, it's a bit more tricky, but it works as well.

I'm not saying it's the most elegant way to do it, but it works rather well (also showing the list of valid options and their default value (in the part that is omitted), but that also wasn't part of the question).

Upvotes: 0

Rolando Isidoro
Rolando Isidoro

Reputation: 5114

Use $#ARGV to get total number of passed argument to a perl script like so:

if (@#ARGV < 4)

I've used before and worked as shown in http://www.cyberciti.biz/faq/howto-pass-perl-command-line-arguments/.

See the original documentation at http://perldoc.perl.org/perlvar.html, it states that:

@ARGV

The array @ARGV contains the command-line arguments intended for the script. $#ARGV is generally the number of arguments minus one, because $ARGV[0] is the first argument, not the program's command name itself. See $0 for the command name.

Upvotes: 1

DVK
DVK

Reputation: 129489

Also, I would STRONGLY suggest using the idiomatic way of processing command line arguments in Perl, Getopt::Long module (and start using named parameters and not position-based ones).

You don't really CARE if you have <3 parameters. You usually care if you have parameters a, b and C present.

As far as command line interface design, 3 parameters is about where the cut-off is between positional parameters (cmd <arg1> <arg2>) vs. named parameters in any order (cmd -arg1 <arg1> -arg2 <arg2>).

So you are better off doing:

use Getopt::Long;
my %args;
GetOptions(\%args,
           "arg1=s",
           "arg2=s",
           "arg3=s",
) or die "Invalid arguments!";
die "Missing -arg1!" unless $args{arg1};
die "Missing -arg2!" unless $args{arg2};
die "Missing -arg3!" unless $args{arg3};

Upvotes: 33

Juan
Juan

Reputation: 1540

You can compare with $#ARGV instead the array @ARGV

if ($#ARGV < 3) { ...

Upvotes: -3

toolic
toolic

Reputation: 62227

Another common way to do that is to use die

die "Usage: $0 PATTERN [FILE...]\n" if @ARGV < 3;

You can get more help on the @ARGV special variable at your command line:

perldoc -v @ARGV

Upvotes: 15

Tim
Tim

Reputation: 14164

Yes, it is fine. @ARGV contains the command-line arguments and evaluates in scalar context to their number.

(Though it looks like you meant @ARGV < 2 or < 1 from your error message.)

Upvotes: 6

Related Questions