NAME
Getopt::App - Write and test your script with ease
SYNOPSIS
The script file
#!/usr/bin/env perl
package My::Script;
use Getopt::App -complete, -signatures;
# See "APPLICATION METHODS"
sub getopt_post_process_argv ($app, $argv, $state) { ... }
sub getopt_configure ($app) { ... }
# run() must be the last statement in the script
run(
# Specify your Getopt::Long options and optionally a help text
'h|help # Output help',
'v+ # Verbose output',
'name=s # Specify a name',
'completion-script # Print autocomplete script',
# Here is the main sub that will run the script
sub ($app, @extra) {
return print generate_completion_script() if $app->{'completion-script'};
return print extract_usage() if $app->{h};
say $app->{name} // 'no name'; # Access command line options
return 42; # Reture value is used as exit code
}
);
Running the script
The example script above can be run like any other script:
$ my-script --name superwoman; # prints "superwoman"
$ echo $? # 42
Testing
use Test::More;
use Cwd qw(abs_path);
use Getopt::App -capture;
# Sourcing the script returns a callback
my $app = do(abs_path('./bin/myapp'));
# The callback can be called with any @ARGV
subtest name => sub {
my $got = capture($app, [qw(--name superwoman)]);
is $got->[0], "superwoman\n", 'stdout';
is $got->[1], '', 'stderr';
is $got->[2], 42, 'exit value';
};
done_testing;
Subcommands
#!/usr/bin/env perl
# Define a package to avoid mixing methods after loading the subcommand script
package My::App::main;
use Getopt::App -complete;
# getopt_subcommands() is called by Getopt::App
sub getopt_subcommands {
my $app = shift;
return [
['find', '/path/to/subcommand/find.pl', 'Find things'],
['update', '/path/to/subcommand/update.pl', 'Update things'],
];
}
# run() is only called if there are no matching sub commands
run(
'h # Print help',
'completion-script # Print autocomplete script',
sub {
my ($app, @args) = @_;
return print generate_completion_script() if $app->{'completion-script'};
return print extract_usage();
}
);
See "getopt_subcommands" and https://github.com/jhthorsen/getopt-app/tree/main/example for more details.
DESCRIPTION
Getopt::App is a module that helps you structure your scripts and integrates Getopt::Long with a very simple API. In addition it makes it very easy to test your script, since the script file can be sourced without actually being run.
Getopt::App also supports infinite nested subcommands and a method for bundling this module with your script to prevent depending on a module from CPAN.
VARIABLES
DEPTH
$Getopt::App::DEPTH
will be increased for each sub command and will be 0
for the first "run".
SUBCOMMAND
$Getopt::App::SUBCOMMAND
will be set to the active sub command element. See also "getopt_subcommands".
APPLICATION METHODS
These methods are optional, but can be defined in your script to override the default behavior.
Order of how the methods are called:
run(@option_spec, $cb)
-> getopt_pre_process_argv(\@argv)
-> getopt_configure()
-> getopt_post_process_argv(\@argv, \%state)
-> $cb
run(@option_spec, $cb)
-> getopt_pre_process_argv(\@argv)
-> getopt_subcommands()
-> getopt_load_subcommand($subcommand, \@argv)
-> getopt_configure()
-> getopt_post_process_argv(\@argv, \%state)
-> $cb
getopt_complete_reply
$app->getopt_complete_reply;
This method will be called instead of the "run" callback when the COMP_LINE
and COMP_POINT
environment variables are set. The default implementation will call "complete_reply" in Getopt::App::Complete.
See also "Completion" under "import".
getopt_configure
@configure = $app->getopt_configure;
This method can be defined if you want "Configure" in Getopt::Long to be set up differently. The default return value is:
qw(bundling no_auto_abbrev no_ignore_case pass_through require_order)
Note that the default "pass_through" item is to enable the default "getopt_post_process_argv" to croak on invalid arguments, since Getopt::Long will by default just warn to STDERR about unknown arguments.
getopt_load_subcommand
$code = $app->getopt_load_subcommand($subcommand, [@ARGV]);
Takes the subcommand found in the "getopt_subcommands" list and the command line arguments and must return a CODE block. The default implementation is simply:
$code = do($subcommand->[1]);
getopt_post_process_argv
$bool = $app->getopt_post_process_argv([@ARGV], {%state});
This method can be used to post process the options. %state
contains a key "valid" which is true or false, depending on the return value from "GetOptionsFromArray" in Getopt::Long.
This method can die
and optionally set $!
to avoid calling the function passed to "run".
The default behavior is to check if the first item in $argv
starts with a hyphen, and die
with an error message if so:
Invalid argument or argument order: @$argv\n
getopt_post_process_exit_value
$exit_value = $app->getopt_post_process_exit_value($exit_value);
A method to be called after the "run" function has been called. $exit_value
holds the return value from "run" which could be any value, not just 0-255. This value can then be changed to change the exit value from the program.
sub getopt_post_process_exit_value ($app, $exit_value) {
return int(1 + rand 10);
}
getopt_pre_process_argv
$app->getopt_pre_process_argv($argv);
This method can be defined to pre-process $argv
before it is passed on to "GetOptionsFromArray" in Getopt::Long. Example:
sub getopt_pre_process_argv ($app, $argv) {
$app->{first_non_option} = shift @$argv if @$argv and $argv->[0] =~ m!^[a-z]!;
}
This method can die
and optionally set $!
to avoid calling the actual "run" function.
getopt_subcommands
$subcommands = $app->getopt_subcommands;
This method must be defined in the script to enable sub commands. The return value must be either undef
to disable subcommands or an array-ref of array-refs like this:
[["subname", "/abs/path/to/sub-command-script", "help text"], ...]
The first element in each array-ref "subname" will be matched against the first command line option, and when matched, the given subcommand item will be passed on to "getopt_load_subcommand" which must return a code-ref, preferably from "run". The sub command will have $Getopt::App::SUBCOMMAND
set to the item found in the list.
getopt_unknown_subcommand
$exit_value = $app->getopt_unknown_subcommand($argv);
Will be called when "getopt_subcommands" is defined but $argv
does not match an item in the list. Default behavior is to die
with an error message:
Unknown subcommand: $argv->[0]\n
Returning undef
instead of dying or a number (0-255) will cause the "run" callback to be called.
EXPORTED FUNCTIONS
capture
use Getopt::App -capture;
my $app = do '/path/to/bin/myapp';
my $array_ref = capture($app, [@ARGV]); # [$stdout, $stderr, $exit_value]
Used to run an $app
and capture STDOUT, STDERR and the exit value in that order in $array_ref
. This function will also capture die
. $@
will be set and captured in the second $array_ref
element, and $exit_value
will be set to $!
.
This function is a very slimmed down alternative to "capture" in Capture::Tiny. The main reason why "capture" exists in this package is that if something inside the $app
throws an exception, then it will be part of the captured $stderr
instead of making capture()
throw an exception.
"capture" in Capture::Tiny is however more robust than this function, so please try Capture::Tiny out in case you find an edge case.
extract_usage
# Default to "SYNOPSIS" from current file
my $str = extract_usage($section, $file);
my $str = extract_usage($section);
my $str = extract_usage();
Will extract a $section
from POD $file
and append command line option descriptions when called from inside of "run". Command line options can optionally have a description with "spaces-hash-spaces-description", like this:
run(
'o|option # Some description',
'v|verbose # Enable verbose output',
sub {
...
},
);
This function will not be exported if a function with the same name already exists in the script.
new
my $app = new($class, %args);
my $app = new($class, \%args);
This function is exported into the caller package so we can construct a new object:
my $app = Application::Class->new(\%args);
This function will not be exported if a function with the same name already exists in the script.
run
# Run a code block on valid @ARGV
run(@option_spec, sub ($app, @extra) { ... });
# For testing
my $cb = run(@option_spec, sub ($app, @extra) { ... });
my $exit_value = $cb->([@ARGV]);
"run" can be used to call a callback when valid command line options are provided. On invalid arguments, warnings will be issued and the program will exit with $?
set to 1.
$app
inside the callback is a hash blessed to the caller package. The keys in the hash are the parsed command line options, while @extra
is the extra unparsed command line options.
@option_spec
are the same options as Getopt::Long can take. Example:
# app.pl -vv --name superwoman -o OptX cool beans
run(qw(h|help v+ name=s o=s@), sub ($app, @extra) {
die "No help here" if $app->{h};
warn $app->{v}; # 2
warn $app->{name}; # "superwoman"
warn @{$app->{o}}; # "OptX"
warn @extra; # "cool beans"
return 0; # Used as exit code
});
In the example above, @extra
gets populated, since there is a non-flag value "cool" after a list of valid command line options.
METHODS
bundle
Getopt::App->bundle($path_to_script);
Getopt::App->bundle($path_to_script, $fh);
This method can be used to combine Getopt::App and $path_to_script
into a a single script that does not need to have Getopt::App installed from CPAN. This is for example useful for sysadmin scripts that otherwise only depends on core Perl modules.
The script will be printed to $fh
, which defaults to STDOUT
.
Example usage:
perl -MGetopt::App -e'Getopt::App->bundle(shift)' ./src/my-script.pl > ./bin/my-script;
import
use Getopt::App;
use Getopt::App 'My::Script::Base', -signatures;
use Getopt::App -capture;
Default
use Getopt::App;
Passing in no flags will export the default functions "extract_usage", "new" and "run". In addition it will save you from a lot of typing, since it will also import the following:
use strict; use warnings; use utf8; use feature ':5.16';
Completion
use Getopt::App -complete;
Same as "Default", but will also load Getopt::App::Complete and import generate_completion_script().
Signatures
use Getopt::App -signatures;
Same as "Default", but will also import "signatures" in experimental. This requires Perl 5.20+.
Class name
package My::Script::Foo; use Getopt::App 'My::Script';
Same as "Default" but will also make
My::Script::Foo
inherit from My::Script. Note that a package definition is required.Capture
use Getopt::App -capture;
This will only export "capture".
COPYRIGHT AND LICENSE
This library is free software. You can redistribute it and/or modify it under the same terms as Perl itself.
AUTHOR
Jan Henning Thorsen - jhthorsen@cpan.org