Getopt::Long: Detect if no Options Passed

186 views Asked by At

How to detect if no options were passed to a script using the GetOpt::Long module?

For instance to output script usage (help) with pod2usage() by default (when the script is called without options).

This general solution doesn't work (because options are not considered arguments):

if (@ARGV == 0) {
  ...
}

I found a related question that was asked 10 years ago, but it seems to be ambiguous (module not specified [ though looks like to be Getopt::Std ], the case with subroutine runs asked specifically), poorly written, and closed.

Best way to run subroutine if no options provided? Perl getOpt


Tried to read on return values:

https://perldoc.perl.org/Getopt::Long#Return-values-and-Errors

Tried to do via @ARGV == 0 as indicated above

3

There are 3 answers

0
Elvin On BEST ANSWER

Based on the answers and further reading elsewhere:

I set my current solution to the following code snippet:

use Getopt::Long;
use Pod::Usage;

my $options;

my $argv_before_options = @ARGV; # Record somewhere

Getopt::Long::Parser
  -> new( config => [ 'bundling', 'no_ignore_case' ] )
    -> getoptions(
        'quite|q' => \$options{quite},
        'help|h' => \$options{help},
       );

my $argv_after_options = @ARGV; # Record for readability

my $options_not_specified =
  $argv_after_getopts == $argv_before_getopts; # Boolean, holding as a variable for readability

if ($options_not_specified or $options{help}) { # Default or -h
  pod2usage();
}

Which I'm satisfied about, although wishing Getopt-Long could have this detection built-in as a return value of some of its method.

Maybe, it's time for a new PR?

0
ikegami On

Here are two ways:

  • Check if @ARGV has the same number of elements before and after the call to GetOptions. If so, no options were passed.

    my $num_args = @ARGV;
    
    GetOptions( ... )
       or usage();
    
    my $num_non_opt_args = @ARGV;
    if ( $num_args == $num_non_opt_args  ) {
       ...
    }
    
  • Before calling GetOptions, check if $ARGV[0] starts with - or + (since GetOptions allows options to start by -, -- and + by default). If so, an option was passed.

    if ( !( @ARGV && $ARGV[0] =~ /^-/ ) ) {
       ...
    }
    
    Getopt::Long::Configure(qw( posix_default ));  # Disalllow `+opt`, for one.
    GetOptions( ... )
       or usage();
    

Both of the above methods count the option terminator -- as an option. If this isn't acceptable, check if the first element of @ARGV is -- and compensate.


That said, it usually makes more sense to check for specific options, which is done by checking if the variables associated with them are defined.

use File::Basename qw( basename );

sub usage {
   if ( @_ ) {
      my $msg = shift;
      chomp( $msg );
      warn( "$msg\n" );
   }

   my $prog = basename( $0 );
   warn( "Use `$prog --help` for help.\n" );
   exit( 1 );
}

usage( "Either `-x` or `-y` must be provided." )
   if !defined( $opt_x ) && !defined( $opt_y );
0
stevieb On

Because options start with either -, -- or +, you can check @ARGV for any element that resembles one of those. Here's a quick and dirty example:

use warnings;
use strict;

use Getopt::Long;

if (! _options_present(@ARGV)) {
    help();
}

my %args;

GetOptions (
    'd|directory=s' => \$args{directory},
    'f|file=s'      => \$args{file},
);

sub help {
    print "You need to send in options!\n";
    exit;
}

sub _options_present {
    my (@args) = @_;
    my $option_present = grep /^(?:--?|\+)\w+/, @args;
    return $option_present;
}