http://qs1969.pair.com?node_id=139049

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

I just came up with the following style to write a script that is called as in foo.pl add --bar or foo.pl mod --baz etc. What do you think of this? (For perfection, rather than simply dieing on a bad parameter it should go through Pod::Usage first, but I didn't bother to add that here.) The main trick, of course, is the new main. Did I outhack myself?
#!/usr/bin/perl -w use warnings; use strict; print "\nfoo script\n\n"; srand time + $$ + $^T; my $action = shift(@ARGV); die "Usage here\n" unless $action; my $execute = new main; my $_action = "_$action"; die "Unknown action '$action'\n\nUsage here\n" unless can $execute $_a +ction; $execute->$_action(); exit; ###################################################################### +######## ###################################################################### +######## use Getopt::Long; # use Other::Modules::Here; sub new { my $class = shift; my %self; GetOptions(\%self, qw(various options here)); # do more stuff with %self here return bless \%self, $class; } sub _add { my $self = shift; # we can see commandline parameters in %$self now } sub _mod { my $self = shift; # ditto } sub _del { my $self = shift; # again } sub foo { # ... } sub bar { # ... }

Replies are listed 'Best First'.
Re: Command line tool coding style?
by chromatic (Archbishop) on Jan 16, 2002 at 02:14 UTC
    That's clever, though you should wrap the method call in an eval block or check can to prevent catastrophic failure if someone asks for an undefined action. Generally I use a dispatch table (hash) of subrefs, but this also works.

    (You can safely omit either -w or warnings, and you don't need srand if you're using a Perl with the warnings pragma.)

      If you look closer, you'll see there is indeed a can $execute $_action up at the top..

      I was going to go with the dispatch table hash approach originally, but what irked me was that I had to put either the entire subroutine code above the body of the main program (when what I wanted is to put them below), or write this => \&do_this hash entries which means maintaining an extra list - neither of which I found satisfactory. After looking at my switch-like construct long and hard enough I noticed I had a bunch of veiled method calls.. hence this was the "natural" solution.

      Are you sure warnings will do an srand for me? (It is a left-over I forgot to remove from the actual script for this example - there's some password generation in there.) I'd be very surprised if it did..

        I did indeed miss can in my first couple of reads.

        This isn't how I'd usually do things, but I'm warming to the approach. It does beat maintaining an extra list and it's prettier than autogenerating a list of methods from the symbol table.

        The warnings pragma will not call srand for you, but any version of Perl recent enough to have that pragma will automatically call srand when necessary. They're not related, and 5.5.3 (without warnings) does call srand automatically. I should have explained it that way initially.

Re: Command line tool coding style?
by perrin (Chancellor) on Jan 16, 2002 at 02:46 UTC
    Why use OO for this?
    if (defined &$action) { &$action; } else { die "Unknown action '$action'\n\nUsage here\n"; }
      Two reaons: strict and the GetOptions() hash. The latter was actually what gave me the idea in the first place. The script started out having a series of
      { do_this(\%options, $table), last if $action eq 'this'; do_that(\%options, $table), last if $action eq 'that'; # ... die "Usage here\n"; }
      Look a bit harder and you'll see method calls in disguise.. With this new approach, all I have to do is write sub _more {} and the script will automatically understand the "more" option - and it works with strict too. (Plus can $execute $_action looks much prettier than defined &$_action IMHO :-))
        You can easilly get by strict if you use this:
        eval('&' . $action . '()'); if( $@ ) { die "Usage\n"; }
        It might be good to check the text of the exception here to make sure it's from a missing sub. You might even be able to do this:
        my $subref = \&{$action}; die "Usage" unless defined &$subref();
        I haven't tried that last though.

        There's just no good reason to use OO here, and it makes the code more confusing and JAPH-ish. Maybe there's something else in your program that justifies OO, but this problem doesn't.

        Incidentally, a nicer way to write that dispatch table from your above comment would be something like this (untested):

        my %dispatch = ( 'this' => \&do_this, 'that' => \&do_that, ); if (defined $dispatch{$action}) { &$dispatch{$action}; } else { die "Usage\n"; }
Re: Command line tool coding style?
by muba (Priest) on Oct 23, 2004 at 20:56 UTC
    It took me a while to figure out what you were doing, but after a while I finally discovered. It looks nice, but... isn't it easier to create a hash with the possible $_actions?

    Update: Hmm... it's an almost 3 years old thread. Should have checked that before posting.




    "2b"||!"2b";$$_="the question"
    Besides that, my code is untested unless stated otherwise.
    One more: please review the article about regular expressions (do's and don'ts) I'm working on.

      Using a hash would be the simple option, indeed, but leads to less generalized code. If I had to do something like this again, I'd do it precisely as perrin suggested.

      Makeshifts last the longest.