2
votes

Is there a way to turn off flags if they are enabled by default in GetOptions?

This is what I want:

  • -verbose 0 turns off verbosity
  • -verbose 1 turns on verbosity
  • -verbose turns on verbosity

Current code (from Getopt::Long):

use Getopt::Long;
my $data = "file.dat";
my $length = 24;
my $verbose = 1;
GetOptions ("length=i" => \$length,   # numeric
            "file=s"   => \$data,     # string
            "verbose"  => \$verbose)  # flag
or die("Error in command line arguments\n");
3

3 Answers

1
votes

It's not the way you describe, but the documentation describes "negatable options" designated by an exclamation mark that let you do this:

my $verbose = 1; # default on
GetOptions ('verbose!' => \$verbose);

This allows --verbose (sets it to 1) or --noverbose (sets it to 0).

1
votes

The Summary of Option Specifications in the documentation for Getopt::Long indicates that you could almost use:

#!/usr/bin/env perl
use strict;
use warnings;

use Getopt::Long;
my $data = "file.dat";
my $length = 24;
my $verbose = 1;
GetOptions ("length=i"  => \$length,   # numeric
            "file=s"    => \$data,     # string
            "verbose:i" => \$verbose)  # optional integer
or die("Error in command line arguments\n");

# Debugging/testing
print "Verbose = $verbose\n";
print "Options:\n";
for my $opt (@ARGV) { print "    $opt\n"; }

The : indicates that the value is optional, and the i indicates it takes an integer.

Sample runs (I called the script gol.pl):

$ perl gol.pl
Verbose = 1
Options:
$ perl gol.pl --verbose 0
Verbose = 0
Options:
$ perl gol.pl --verbose=0
Verbose = 0
Options:
$ perl gol.pl --verbose 1
Verbose = 1
Options:
$ perl gol.pl --verbose gooseberry
Verbose = 0
Options:
    gooseberry
$ perl gol.pl --verbose
Verbose = 0
Options:
$

There's an 'almost' at the top. As ThisSuitIsBlackNot correctly points out, this sets $verbose to zero when the argument is omitted, which is not what you want.

Your interface is curious. Are you sure you wouldn't be better off with:

--verbose    # Enables verbose mode
--noverbose  # Disables verbose mode

You can then use "verbose!" to handle that. Also, since verbose mode is enabled by default, there's really no need to support --verbose; there's point in having --verbose 0 to turn it off, or --noverbose, and maybe point in allowing --verbose 9 for extra verbose, etc. You need to think about whether your design is truly appropriate.

1
votes

This might be a little too complex, but you can use an optional value combined with a custom subroutine:

use strict;
use warnings;
use 5.010;

use Getopt::Long;

sub make_handler {
    my $verbose = shift;

    return sub {
        my ($opt_name, $opt_value) = @_;

        die "$opt_name must be 0 or 1" if $opt_value !~ /^(?:0|1)?$/;

        if ($opt_value eq '') {
            $$verbose = 1;
        }
        else {
            $$verbose = $opt_value;
        }
    }
}

my $verbose = 1;
my $handler = make_handler(\$verbose);

GetOptions("verbose:s"  => $handler) or die "Error in command line arguments";

say $verbose;

Output:

$ ./foo 
1
$ ./foo --verbose
1
$ ./foo --verbose 0
0
$ ./foo --verbose 1
1
$ ./foo --verbose bar
verbose must be 0 or 1 at ./foo line 15.
Error in command line arguments at ./foo line 29.

Note that I used a closure to avoid global variables, since Getopt::Long doesn't do anything with the return value of custom subroutines and doesn't allow you to pass in the variable you want to set.