The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Getopt::App - Write and test your script with ease

SYNOPSIS

The script file

  #!/usr/bin/env perl
  package My::Script;
  use Getopt::App -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',

    # Here is the main sub that will run the script
    sub ($app, @extra) {
      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;

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.

This module is currently EXPERIMENTAL, but is unlikely to change much.

APPLICATION METHODS

These methods are optional, but can be defined in your script to override the default behavior.

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)

The default return value is currently EXPERIMENTAL.

getopt_load_subcommand

  $code = $app->getopt_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 argument passed to the script, and when matched the "sub-command-script" will be sourced and run inside the same perl process. The sub command script must also use Getopt::App for this to work properly.

See https://github.com/jhthorsen/getopt-app/tree/main/example for a working example.

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 dieing 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 $!.

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 $obj = new($class, %args);
  my $obj = 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(@rules, sub ($app, @extra) { ... });

  # For testing
  my $cb = run(@rules, sub ($app, @extra) { ... });
  my $exit_value = $cb->([@ARGV]);

"run" can be used to call a callback when valid command line options is provided. On invalid arguments, warnings will be issued and the program 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.

@rules 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 otherwize 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';
  • 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