CLI::Simple - a minimalist object oriented base class for CLI applications
#!/usr/bin/env perl
package MyScript;
use strict;
use warnings;
use CLI::Simple::Constants qw(:booleans :chars);
use CLI::Simple qw($AUTO_HELP $AUTO_DEFAULT);
use parent qw(CLI::Simple);
sub execute {
my ($self) = @_;
# retrieve a CLI option
my $file = $self->get_file;
...
}
sub list {
my ($self) = @_
# retrieve a command argument
my ($file) = $self->get_args();
...
}
sub main {
# Disable auto-default for single commands, enable auto-help
$AUTO_DEFAULT = 0;
$AUTO_HELP = 1;
my $cli = MyScript->new(
option_specs => [ qw( help format=s file=s) ],
default_options => { format => 'json' }, # set some defaults
extra_options => [ qw( content ) ], # non-option, setter/getter
commands => { execute => \&execute, list => \&list, }
alias => { options => { fmt => format }, commands => { ls => list } },
);
return $cli->run();
}
exit main();
1;
Tired of writing the same 'ol boilerplate code for command line
scripts? Want a standard, simple way to create a Perl script that
takes options and commands? CLI::Simple makes it easy to create
scripts that take options, commands and arguments.
For common constant values (like $TRUE, $DASH, or $SUCCESS), see
CLI::Simple::Constants, which pairs naturally with this module.
This documentation refers to version 1.0.10.
New package variables $AUTO_HELP and $AUTO_DEFAULT
These new package variables allow finer grained control over default behaviors when no command is provided on the command line. These changes were made to allow for a more flexible lifecycle. See "The init-run Lifecycle".
CLI::Simple would print help
information if it was available and if there was no command provided
on the command line. That behavior is now controlled by $AUTO_HELP.CLI::Simple would run a
default command if only one command was defined and there was no
command on the command line. That behavior is now controlled by
$AUTO_DEFAULT.Command line scripts often take options, sometimes a command and
perhaps arguments to those commands. For example, consider the
script myscript that takes options and implements a few commands
(send-message, receive-message) that also take arguments.
myscript [options] command args
or
myscript command [options] args
Examples:
myscript --foo bar --log-level debug send-message "Hello World" now
myscript --bar --log-level info receive-message
Using CLI::Simple to implement this script looks like this...
package MyScript;
use parent qw(CLI::Simple);
caller or __PACKAGE__->main();
sub send_message {...}
sub default {...}
sub receive_message {...}
sub main {
return __PACKAGE__->new(
option_specs => [
qw(
foo=s
bar
log-level
)
],
commands => {
send => \&send_message,
receive => \&receive_message,
},
)->run;
}
1;
CLI::Simple is intentionally minimalist. It provides just enough
structure to build command-line tools with subcommands, option
parsing, and help handling -- but without enforcing any particular
framework or lifecycle.
This module is not App::Cmd, MooseX::Getopt, or a full application toolkit. Instead, it offers:
run() dispatcherGetopt::LongLog::Log4perlinit() for setup and validationThe philosophy is: provide just enough infrastructure, then get out of your way.
CLI::Simple does not impose a validation model. You may:
Getopt::Long features (e.g., type constraints, default values)init()The lifecycle is explicit and under your control. You decide how much structure you want to add on top of it.
CLI::Simple is ideal for:
For more advanced features - like command trees, plugin support, or interactive CLI handling - consider heavier modules like App::Cmd, CLI::Framework, or MooX::Options.
CLI::Simple is built on a flexible, two-phase lifecycle that
separates application setup from command execution.
Phase 1: Initialization (new => init)
When your script calls CLI::Simple->new()>, the constructor parses
all command-line arguments and then immediately calls your init()
method.
Inside init(), your application has full access to the parsed options
and arguments. This phase is the ideal hook for all final setup tasks,
such as:
--config option.$self->command('new_default')). This init() phase always runs as part of object construction.
Phase 2: Execution (run)
After the new() method returns your object, your script then calls
the run() method. This method is responsible for dispatching to
the currently set command.
By design, CLI::Simple does not impose a default command.
This provides total flexibility for the application author:
help when no command is given), you can set
$AUTO_HELP, explicitly set the default command in the command
hash you pass to the constructor or uset command() to set one
inside the init() method.run() will simply do nothing and return cleanly if no command
is provided on the command line.This "no default by default" behavior is what enables a powerful "setup-only" execution mode. A user can run your script without specifying a command. This will:
new() / init() phase, performing all setup.run(), which will find no command and exit cleanly.This provides an ideal hook for applications that need to perform
"on-demand initialization" (e.g., seeding a database, authenticating)
by checking for a specific flag inside init(), without also
triggering an unwanted command.
$AUTO_HELP and $AUTO_DEFAULTTwo package variables can be used to further control the lifecycle. By default, the framework provides no default command as explained in the sections above. Some scripters may want default behaviors that assume a command or provide usage if no command is provided.
$AUTO_HELP
Set the package variable $AUTO_HELP to a true value if you want
CLI::Simple to provide help when no command is provided.
default: false
$AUTO_DEFAULT
Set the package variable $AUTO_DEFAULT to a true value if you want
CLI::Simple to automatically select a command if you have only 1
command defined and no command is provided on the command line. When
true, it will prepend the single command name to the argument list,
allowing any subsequent arguments to be correctly parsed as args for
that command.
default: false
CLI::Simple does not define its own constants directly, but it is often used
in conjunction with CLI::Simple::Constants, which provides a collection of
exportable values commonly needed in command-line scripts.
These include:
$TRUE, $FALSE, $SUCCESS, and $FAILURE$COLON, $DASH, $EQUALS_SIGN, etc.To use them in your script:
use CLI::Simple::Constants qw(:all);
new( args )
Instantiates a new CLI::Simple instance, parses options, optionally
initializes logging, and makes options available via dynamically
generated accessors.
Note: The new() constructor uses Getopt::Long's GetOptions,
which directly modifies @ARGV by removing any recognized
options. The remaining elements of @ARGV are treated as the command
name and its arguments.
args is a hash or hash reference containing the following keys:
abbreviations
A boolean that determines whether abbreviated command names are allowed.
When true, the run() method will treat the provided command as a prefix
and compare it to the keys in the command hash. If exactly one match is
found, it will be used. If more than one match is found, or if no match is
found, run() will throw an exception.
This allows for convenient shorthand like:
mytool disable-sched # expands to 'disable-scheduled-task'
default: false
commands (required)
A hash mapping command names to either a subroutine reference or an array reference.
If an array reference is used, the first element must be a subroutine reference and the second should be a valid log level. (See "Per Command Log Levels".)
Example:
{
send => \&send_message,
receive => \&receive_message,
list_messages => [ \&list_messages, 'error' ],
}
If your script does not use command names, you may set a default key
to the subroutine or method to run:
{ default => \&main }
If no default is provided, the behavior is controlled by the
$AUTO_DEFAULT and $AUTO_HELP package variables.
Setting $AUTO_DEFAULT to true will when your commands hash
contains only a single command, will cause that command to be run
automatically when no command name is given on the command line. This
allows you to treat the program like a single-command tool, where
arguments can be passed directly without explicitly naming the
command.
default_options (optional)
A hash reference providing default values for options. These values apply if the corresponding option is not given on the command line.
extra_options (optional)
An array reference of names for additional accessors you want to create,
even if they are not part of option_specs.
Example:
extra_options => [ qw(foo bar baz) ]
option_specs (optional)
An array reference of option specifications, as accepted by Getopt::Long. These define the command-line options your program recognizes.
command
command(command)
Get or sets the command to execute. Usually this is the first argument
on the command line after all options have been parsed. There are
times when you might want to override the argument. You can pass a new
command that will be executed when you call the run() method.
commands
commands(command, handler)
Returns the hash you passed in the constructor as commands or can
be used to insert a new command into the commands hash. handler
should be a code reference.
commands(foo => sub { return 'foo' });
Execute the script with the given options, commands and arguments. The
run method interprets the command line and passes control to your
command subroutines. Your subroutines should return a 0 for success
and a non-zero value for failure. This error code is passed to the
shell as the script return code.
Return the arguments that follow the command.
get_args(NAME, ... ) # with names
get_args() # raw positional args
With names:
- In scalar context, returns a hash reference mapping each NAME to the
corresponding positional argument.
- In list context, returns a flat list of (name = value)> pairs.
With no names:
- Returns the command's positional arguments (array in list context; array reference in scalar context).
Example:
sub send_message {
my ($self) = @_;
my %args = $self->get_args(qw(message email));
_send_message($args{message}, $args{email});
}
When you call get_args with a list of names, values are assigned in
order: the first name gets the first argument, the second name gets the
second argument, and so on. If you only want specific positions, you may
use undef as a placeholder:
my %args = $self->get_args('message', undef, 'cc'); # args 1 and 3
If there are fewer positional arguments than names, the remaining names
are set to undef. Extra positional arguments (beyond the provided
names) are ignored.
If you define your own init() method, it will be called by the
constructor. Use this method to perform any actions you require before
you execute the run() method.
You can pass the necessary parameter required to implement your command line scripts in the constructor or some people prefer to see them clearly defined in the code. Accordingly, you can use package variables with the same name as the constructor arguments (in upper case).
our $OPTION_SPECS = [
qw(
help|h
log-level=s|L
debug|d
)
];
our $COMMANDS = {
foo => \&foo,
bar => \&bar,
};
Command-line options are defined using Getopt::Long-style
specifications. You pass these into the constructor via the
option_specs parameter:
my $cli = CLI::Simple->new(
option_specs => [ qw( help|h foo-bar=s log-level=s ) ]
);
In your command subroutines, you can access these values using automatically generated getter methods:
$cli->get_foo();
$cli->get_log_level();
Option names that contain dashes (-) are automatically converted to
snake_case for the accessor methods. For example:
option_specs => [ 'foo-bar=s' ]
...results in:
$cli->get_foo_bar();
If your commands accept positional arguments, you can retrieve them
using the get_args method.
You may optionally provide a list of argument names, in which case the arguments will be returned as a hash (or hashref in scalar context) with named values.
Example:
sub send_message {
my ($self) = @_;
my %args = $self->get_args(qw(phone_number message));
send_sms_message($args{phone_number}, $args{message});
}
If you call get_args() without any argument names, it simply
returns all remaining arguments as a list:
my ($phone_number, $message) = $self->get_args;
Note: When called with names, get_args returns a hash in list
context and a hash reference in scalar context.
By default, CLI::Simple will exit if GetOptions returns a false
value, indicating an error while parsing options. You can override this
behavior in one of two ways:
Set $CLI::Simple::EXIT_ON_ERROR to a false value.
This disables automatic exiting and lets your program decide what to do after an option-parsing failure.
Provide an error_handler callback in the constructor.
my $cli = CLI::Simple->new(
commands => \%commands,
default_options => \%default_options,
extra_options => \@extra_options,
option_specs => \@option_specs,
abbreviations => $TRUE,
error_handler => sub {
my ($msg) = @_;
print {*STDERR} $msg;
return $TRUE; # continue processing
},
);
The error handler is called with the error message from GetOptions.
It must return a boolean: a true value allows processing to continue,
while a false value causes CLI::Simple to exit immediately.
To assign default values to your options, pass a hash reference as the
default_options argument to the constructor. These values will be
used unless explicitly overridden by the user on the command line.
Example:
my $cli = CLI::Simple->new(
default_options => { foo => 'bar' },
option_specs => [ qw(foo=s bar=s) ],
commands => {
foo => \&foo,
bar => \&bar,
},
);
Defaulted options are accessible through their corresponding getter methods, just like options set via the command line.
To provide built-in usage/help output, include a =head1 USAGE
section in your script's POD:
=head1 USAGE
usage: myscript [options] command args
Options
-------
--help, -h Display help
...
If the user supplies the command help, or the --help option,
CLI::Simple will display this section automatically:
perl myscript.pm --help
perl myscript.pm help
If you need full control over the help output, you can define a custom
help method and assign it as a command:
commands => {
help => \&help,
...
}
This is useful if your module follows the modulino pattern and you
want to present usage information that differs from the embedded
POD. Without a custom handler, CLI::Simple defaults to displaying the
USAGE POD section.
All command-line options are automatically available through getter
methods named get_*.
If you need to create additional accessors (getters and setters) for
values that are not derived from the command line, use the
extra_options parameter.
This is useful for passing runtime configuration or computed values throughout your application.
Example:
my $cli = CLI::Simple->new(
default_options => { foo => 'bar' },
option_specs => [ qw(foo=s bar=s) ],
extra_options => [ qw(biz buz baz) ],
commands => {
foo => \&foo,
bar => \&bar,
},
);
This will generate get_biz, set_biz, get_buz, etc., for
internal use.
CLI::Simple integrates with Log::Log4perl to provide structured
logging for your scripts.
To enable logging, call the class method use_log4perl() in your
module or script:
__PACKAGE__->use_log4perl(
level => 'info',
config => $log4perl_config_string
);
If you do not explicitly include a log-level option in your
option_specs, CLI::Simple will automatically add one for you.
Once enabled, you can access the logger instance via:
my $logger = $self->get_logger;
This logger supports the standard Log4perl methods like info,
debug, warn, etc.
Some commands may require more verbose logging than others. For example, certain commands might perform complex actions that benefit from detailed logs, while others are designed solely to produce clean, structured output.
To assign a custom log level to a command, use an array reference as the value for that command in the commands hash passed to the constructor.
The array reference should contain at least two elements:
Example:
CLI::Simple->new(
option_specs => [qw( help format=s )],
default_options => { format => 'json' }, # set some defaults
extra_options => [qw( content )], # non-option, setter/getter
commands => {
execute => \&execute,
list => [ \&list, 'error' ],
}
)->run;
TIP: add other elements to the array for your command to process.
How do I execute startup code before my command runs?
Implement an init() method in your class. The new() constructor
will invoke this method before returning and before run() is
executed.
Your init() method will have access to all options and
arguments. Logging will also be initialized, so you can use
get_logger() to emit messages.
Do I need to implement commands?
No. If your script doesn't support multiple commands, you can specify
a default key instead:
commands => { default => \&main }
Must I subclass CLI::Simple?
No. You can use it procedurally or functionally.
How do I turn my class into a script?
Use the modulino pattern: create a class that checks whether it is being invoked directly:
package MyScript;
caller or __PACKAGE__->main();
sub main {
...
}
This lets the file be used as both a module and an executable script.
How do the helper scripts work?
This distribution includes two scripts to help with modulino-style scripts:
modulino
A generic launcher that runs a Perl module as a script.
create-modulino.pl
Creates a symbolic link or wrapper script to make your modulino-based module runnable from the command line.
Example:
sudo create-modulino.pl Foo::Bar foo-bar
This creates an executable called foo-bar that loads and invokes
Foo::Bar as a modulino script.
CLI::Simple lets you define short, human-friendly aliases for both
option names and command names. Use the alias parameter to new():
my $app = CLI::Simple->new(
option_specs => [ qw(config=s verbose!) ],
commands => { list => \&list, execute => \&execute },
alias => {
options => { cfg => 'config', v => 'verbose' },
commands => { ls => 'list' }
},
);
Spec tail is copied automatically
You only name the canonical option in option_specs. For each alias,
CLI::Simple finds the canonical option's spec tail (for example
=s, :i, !, +) and appends it to the alias. In the example
above, cfg behaves as if you had written cfg=s, and v behaves
as if you had written v!.
Note: If your option includes a one-letter short-cut and the alias does not start with the same letter it will not be automatically enabled as a short-cut.
Accessors are created for both names
Accessors are generated from all option names (canonical and aliases),
with '-' normalized to '_'. In the example, both get_config() and
get_cfg() are available.
Values are mirrored after parsing
After option parsing and normalization, values are mirrored so either name can be used consistently. If both the canonical name and its alias are provided on the command line, the alias wins and becomes the final value for both names.
No duplicate injection
If the alias already exists in option_specs, it will not be injected
again; value mirroring still occurs.
Errors are explicit
If an alias points at a canonical option that does not exist,
CLI::Simple croaks with a clear error.
Case sensitivity
Getopt::Long is used with :config no_ignore_case, so option names
(and therefore aliases) are case sensitive by default.
Simple mapping
Provide alias = { commands => { alias => canonical } }> to map an alias
to an existing command. In the example, ls dispatches to the list
command.
Applied before abbreviations
Aliases are installed before command abbreviation resolution. If you enable abbreviations, they apply to the full set of command names, including any aliases.
Errors are explicit
If an alias points at a command that does not exist, CLI::Simple croaks
with a clear error.
# Using an option alias
script.pl --cfg app.json execute
# Using a command alias
script.pl ls
After parsing, both get_config() and get_cfg() will return the
same value. If the user passes both --config and --cfg, the value
from --cfg (the alias) is used.
Keep the canonical spec single-named
Define a single canonical name in option_specs and add other spellings
via alias. Avoid multi-name specs like config|cfg=s; use alias
instead.
Document your precedence
If you prefer the alias name to win when both are supplied, enforce that in your application or adjust the mirroring order. By default, the canonical name wins.
When you execute the run() method it passes control to the method
that implements the command specified on the command line. Your method
is expected to return 0 for success or an error code that you can the
pass to the shell on exit.
exit CLI::Simple->new(commands => { foo => \&cmd_foo })->run();
CLI::Simple uses conventional exit codes so that calling scripts
can distinguish between normal completion and error conditions.
'0'
Successful completion of a command (SUCCESS).
'1'
General usage error, such as --help display via pod2usage, or an
invalid command line (FAILURE).
'2'
Option parsing failure, such as an unrecognized option or invalid
argument (also reported as FAILURE).
Any other code
If a user-supplied command callback explicitly calls exit() or
returns a numeric value other than 0 - 2, that code is passed through
unchanged to the shell. This allows application-specific exit codes.
Run the shell script that comes with the distribution to output a working example.
cli-simple-example > example.pl
This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself. See https://dev.perl.org/licenses/ for more information.
Getopt::Long, CLI::Simple::Utils, Pod::Usage, App::Cmd, CLI::Framework, MooX::Options
Rob Lauer - bigfoot@cpan.org