rockneybot has asked for the wisdom of the Perl Monks concerning the following question:

The Getopt::Long docs have a too trivial example of how to run a subroutine by setting an option. Here's what the doc says:

User-defined subroutines to handle options Ultimate control over what should be done when (actually: each +time) an option is encountered on the command line can be achieved by + designating a reference to a subroutine (or an anonymous subroutine) + as the option destination. When GetOptions() encounters the option, +it will call the sub-routine with two or three arguments. The first a +rgument is the name of the option. For a scalar or array destination, + the second argument is the value to be stored. For a hash destinatio +n, the second arguments is the key to the hash, and the third argumen +t the value to be stored. It is up to the subroutine to store the val +ue, or do whatever it thinks is appropriate. A trivial application of this mechanism is to implement options + that are related to each other. For example: my $verbose = ''; # option variable with default value (f +alse) GetOptions ('verbose' => \$verbose, 'quiet' => sub { $verbose = 0 }); Here "--verbose" and "--quiet" control the same variable $verbo +se, but with opposite values. If the subroutine needs to signal an error, it should call die( +) with the desired error message as its argument. GetOptions() will c +atch the die(), issue the error message, and record that an error res +ult must be returned upon completion. If the text of the error message starts with an exclamantion ma +rk "!" it is interpreted specially by GetOptions(). There is current +ly one special command implemented: "die("!FINISH")" will cause GetOp +tions() to stop processing options, as if it encountered a double das +h "--".

So, here the doc shows usage with an anonymous subroutine. I'm new to Perl and it would be helpful for me to see usage of predefined subroutines with Getopt::Long. If possible, how do I pass arguments to the subroutine? I haven't been able to find any Perl scripts that use Getopt::Long either. Can you share yours with me, so I can glean the information I need? Thank you.

Replies are listed 'Best First'.
Re: Getopt::Long subroutine usage
by atcroft (Abbot) on Jan 05, 2005 at 02:24 UTC

    Maybe this example will help:

    #!/usr/bin/perl use strict; use warnings; use Getopt::Long; $| = 1; my $blah = 0; if ( scalar( grep( /^-/, @ARGV ) ) ) { GetOptions( 'blah' => sub { $blah = ( $blah ? 0 : 1 ); }, 'help' => \&help ); } printf <<RESULTS, $blah; Blah: %d RESULTS sub help { print <<HELPTEXT; $0 [-blah] [-help] -blah - do something -help - display this help text HELPTEXT exit; }

    In this contrived example, using the -help option will execute the &help routine, while the -blah option will execute the anonymous subroutine (which in this contrived example flips the value of $blah between true and false).

    Hope that helps.

      Well, there is some cool stuff going on there, and thanks for the lightning response time, but I guess I was looking for a more indepth example. Perhaps the functionality I'm looking for in the module just isn't available and will be up to me to code. I'll try to outline the problem I'm encountering. Here's the script I'm working on currently. Please don't laugh. I'm new.

      The script works, but it's clunky. I wanted to be able to reference a subroutine with the --prep option for instance, but the --prep option uses two scalars --device and --label. Those two variables can be set on the command line. If I reference --prep as a subroutine instead of using my cludgey IF expressions, --disk and --label MUST preceed --prep on the command line. I was hoping to be able to require the dependent options within the getopt module, but I'm not sure if it's possible or what the syntax would be. I am guessing that it isn't possible or that I will have to think harder about what's going on here and learn more about programming in general. :-D

      2005-01-05 Janitored by Arunbear - added readmore tags, as per Monastery guidelines

        You have an interesting case here. Personally, I don't see anything wrong with the approach you've started with, and I think trying to stuff subroutine refs into the GetOpt::Long usage to handle option dependencies is just going to obfuscate things. You don't need to go there, IMO. (I would especially advise against any approach the requires the options to be given in a specific order; this goes against the nature of option usage as most command-line users understand it.)

        Apart from that, I have just a few nit-picks:

        • Running the script with no args looks like a no-op; this should be treated the same as using the "-h|--help" option.
        • You might consider working on the pod a bit... see if you can indicate some of the option dependencies in the synopsis, and/or re-order the list of options so the dependencies are more salient and make sense on first glance.
        • For heaven's sake, provide an appropriate DESCRIPTION that at least gives an idea about what happens when the various settings are activated.
        • Your "make_dirs" sub could be using the Perl-internal "mkdir" and "chmod" functions, rather than running a bunch of system calls, and I'm not sure what you gain by using  !system "ls ..." or die as opposed to, say,  ( -d "..." ) or die
        • You have a "get_line" sub that is never called; I think it's very good that you never actually use this sub, and it would be fine to remove it from the script.
        I was hoping to be able to require the dependent options within the getopt module

        This is how I handle option dependecies. Whether it's clearer than your method is largely a matter of taste. You keep a dependecy near the code that mandates it. I group all of them near the GetOptions call.

        use Getopt::Long; use Pod::Usage; my %opt; GetOptions( \%opt, 'help', 'man', 'V|version', 't|timeout=i', 'H|hostname=s', 'p|port=i', 'U|uri=s', ) or pod2usage(2); # standard opt handling; $opt{h} && pod2usage(1); $opt{man} && pod2usage( -exitstatus => 0, -verbose => 2 ); $opt{V} && version(); # option dependencies ( defined($opt{U}) xor defined($opt{H}) ) || pod2usage(1); defined($opt{H}) && defined($opt{p}) || pod2usage(1); # ... go on your merry way knowing dependencies are satisfied __END__ =head1 NAME opttest - Check out Getopt::Long and Pod::Usage =head1 SYNOPSIS opttest [options] -U uri - or - opttest [options] -H <hostname> -p <port> Options: U | -uri uri to check H | -hostname hostname to check p | -port tcp port to check (required with hostname!) ... =head1 DESCRIPTION Optest provides some basic option handling and demonstrates how to handle option dependencies for most-likely cases. =cut

        Adding some feedback to the user also helps the code self-document, but does create some clutter:

        ( defined($opt{U}) xor defined($opt{H}) ) || do { print "Only one of 'hostname' or 'uri' must be specified\n"; pod2usage(1); }; defined($opt{H}) && defined($opt{p}) || do { print "Hostname option requires port option\n"; pod2usage(1); };

        --Solo

        --
        You said you wanted to be around when I made a mistake; well, this could be it, sweetheart.
Re: Getopt::Long subroutine usage
by William G. Davis (Friar) on Jan 05, 2005 at 02:26 UTC

    The sub {...} thing returns a reference to an anonymous subroutine. You can take a reference to any subroutine, though, using the backslash operator and the ampersand, like this:

    my $sub_ref = \&subroutineName;

    See perlreftut and perlref for more information.

Re: Getopt::Long subroutine usage
by legato (Monk) on Jan 05, 2005 at 17:07 UTC
    GetOptions('quiet' => sub{ launch_on_quiet(@_) } ); sub launch_on_quiet { # Stuff do do when --quiet is passed }
    That's one way to do it, if your sub is simple. Otherwise, you'll have to work with code references. The value given to --quiet will be passed to your sub via @_

    Anima Legato
    .oO all things connect through the motion of the mind