How to implement subcommand using App::Cmd module?

Viewed 250

I use App::Cmd for building my CLI apps.

Is it possible to implement subcommand like docker network inspect ? (assuming that inspect is subcommand)

I have following code for implementing command foo bar

$ cat bin/foo
#!/usr/bin/perl
use Foo;
Foo->run;

$ cat lib/Foo.pm 
package Foo;
use App::Cmd::Setup -app;
1;

$ cat lib/Foo/Command/bar.pm 
package Foo::Command::bar;
use App::Cmd::Setup -command;

sub execute {
    print "Hello world\n";
}

1;

I implemented subcommand foo bar baz by modifying lib/Foo/Command/bar.pm as:

sub execute {
    my ( $self, $opt, $args ) = @_;    
    my $subcommand = $args->[0];

    if ( $subcommand eq 'baz' ) {
        print "Subcommand baz is working\n";
    }

    ...
}

Is there any better way ?

Big minus of $args->[0] approach is that you can not auto generate --help (foo bar will show same help as foo bar baz)

In other words, App::Cmd is sharpen for <bin_name> <command> [-?h] [long options...] call syntax, but I need <bin_name> <command> <subcommand> [-?h] [long options...] syntax.

P.S. I created Github repository for experiments

1 Answers

It took me a while to find some example code on Github ([1], [2]) to figure this out. For the foo bar baz use case, you need the following:

# Foo.pm is unchanged from your code

# Bar.pm is no longer a command module
$ cat lib/Foo/Command/Bar.pm 
package Foo::Command::Bar;

use base qw/App::Cmd::Subdispatch/;
use constant plugin_search_path => __PACKAGE__;

# no need for execute(), this module only dispatches subcommands
# abstract() and show_desc() are still supported

1;

# Finally, a subcommand like `baz` needs its own script Baz.pm 
# (mind the file location)
$ cat lib/Foo/Command/Bar/Baz.pm 
package Foo::Command::Bar::Baz;
use App::Cmd::Setup -command;

sub execute {
    print "Hello world\n";
}

1;
Related