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