in reply to Getopt::Long subroutine usage

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.

Replies are listed 'Best First'.
Re^2: Getopt::Long subroutine usage
by rockneybot (Novice) on Jan 05, 2005 at 02:36 UTC
    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 guess the path I'm taking will work. I just assumed that someone had been down this path before and had come up with a comprehensive way of handling options in a multi-purpose script. I definitely agree that the script should not require that options be given in a specific order. Thanks for the nit-picks! I plan to add a --help option or use auto_help built in to the module. The POD needs work, that's for certain. Hah! The description was nabbed straight from the Getopt::Long docs. I'll change it. Um, I forgot about mkdir and chmod builtins. The ls function is necessary, but the die is not. Since I'm using automount, the filesystem isn't mounted until the path is read. I suppose I could mount the filesystem to a temp location, but that seems like more work, more lines of code, more cleanup. The get_line sub and the remove_line subs are not currently being used. I should have removed them before posting.
      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.
        Thank you. This will help me. I learn best by example for some reason. I would rather use a method for grouping the dependencies near the top. My method comes from a shallow bag of tricks.