NAME

CLI::Cmdline - Minimal command-line parser with short/long options and aliases in pure Perl

VERSION

1.25

SYNOPSIS

use CLI::Cmdline qw(parse);

my $switches = '-v -q -h|help --dry-run';
my $options  = 'input --output --config --include';

# only define options which have no default value 0 or '';
my %opt = (
    v       => 1,          # switch, will be incremented on each occurrence
    include => [],         # multiple values allowed
    config  => '/etc/myapp.conf',
);

CLI::Cmdline::parse(\%opt, $switches, $options)
    or die "Usage: $0 [options] <files...>\nTry '$0 --help' for more information.\n";

# @ARGV now contains only positional arguments
die " .... "   if $#ARGV < 0 || $ARGV[0] ne 'file.txt';

DESCRIPTION

Tiny, zero-dependency command-line parser supporting short/long options, aliases, bundling, repeated switches, array collection, and -- termination.

  • Short options: -v, -vh, -header

  • Long options: --verbose, --help

  • Long options with argument: --output file.txt or --output=file.txt

  • Aliases via |

  • Optional leading - or -- in specification strings

  • Single-letter bundling: -vh, -vvv, -vd dir

  • Switches counted on repeat

  • Options collect into array only if default is ARRAY ref

  • -- ends processing

  • On error: returns 0, restores @ARGV

  • On success: returns 1

EXAMPLES

Minimal example - switches without explicit defaults

You do not need to pre-define every switch with a default value. Missing switches are automatically initialized to 0.

my %opt;
parse(\%opt, '-v -h -x')
    or die "usage: $0 [-v] [-h] [-x] files...\n";

# After parsing ./script.pl -vvvx file.txt
# %opt will contain: (v => 3, h => 0, x => 1)
# @ARGV == ('file.txt')

Required Options

To make an option required, declare it with an empty string default and check afterward:

my %opt = ( mode => 'normal');
parse(\%opt, '', '--input --output --mode')
    or die "usage: $0 --input=FILE [--output=FILE] [--mode=TYPE] files...\n";

die "Error: --input is required\n"   if ($opt{input} eq '');

Collecting multiple values, no default array needed

If you want multiple occurrences but don't want to pre-set an array:

my %opt = (
    define => [],        # explicitly an array ref
);

parse(\%opt, '', '--define')
    or die "usage: $0 [--define NAME=VAL ...] files...\n";

# ./script.pl --define DEBUG=1 --define TEST --define PROFILE
# $opt{define} == ['DEBUG=1', 'TEST', 'PROFILE']

# Alternative: omit the default entirely (parser will not auto-create array)
# If you forget the [] default, repeated --define will overwrite the last value.

Realistic full script with clear usage message

#!/usr/bin/perl
use strict;
use warnings;

use Data::Dumper $Data::Dumper::Sortkeys = 1;
use CLI::Cmdline qw(parse);

my $switches = '-v|verbose -q|quiet --help --dry-run -force|f';
my $options  = '-input|i -output -mode -tag';
my %opt      = ( v => 1, mode => 'normal', tag => [] );   # tag = multiple tags allowed

CLI::Cmdline::parse(\%opt, $switches, $options)
        or die "Try '$0 --help' for more information. ARGV = @ARGV\n";

#  --- check if ARGV is filled or help is required
Usage()        if $#ARGV < 0 || $opt{help};
die "Error: --input is required. See $0 --help for usage.\n"   if ($opt{input} eq '');

my $verbose = $opt{v} - $opt{q};
print "Starting processing (verbose $verbose)...\n" if $verbose > 0;

print Dumper(\%opt);
print "ARG = [".join('] [',@ARGV)."]\n";
exit 0;

sub Usage {
    print <<"USAGE";
Usage  : $0 [options] --input=FILE [files...]
Options:
  -v|verbose                Increase verbosity (repeatable)
  -q|quiet                  Suppress normal output
  --dry-run                 Show what would be done
  -f|force                  Force operation even if risky
  --input=FILE              Input file (required)
  --output=FILE             Output file (optional)
  --mode=MODE               Processing mode, default: $opt{mode}
  --tag=TAG                 Add a tag (multiple allowed)
  --help                    Show this help message

Example:
  $0 --input=data.csv -vvv file1.txt
  $0 --input=data.csv --tag=2026 --tag=final -vv file1.txt
  $0 --input=data.csv -quiet  file1.txt
  $0 -input=data.csv -dry-run file1.txt   # not a long tag with =
  $0 -input data.csv -vf file1.txt
  $0 -vfi data.csv file1.txt
  $0 -vif data.csv file1.txt              # option not at the end
  $0 file1.txt                            # missing input error
  $0 --help

USAGE
    exit 1;
}

Using -- to pass filenames starting with dash

my %opt;
parse(\%opt, '-r')
    or die "usage: $0 [-r] files...\n";

# Command line:
./script.pl -r -- -hidden-file.txt another-file

# Results:
# $opt{r} == 1
# @ARGV == ('-hidden-file.txt', 'another-file')

Bundle Behavior with Unknown Options

When an unknown option appears in a short-option bundle, the entire bundle is rejected and @ARGV is restored. However, any switches processed BEFORE the unknown option will have been incremented:

my %opt = (v => 0);
parse(\%opt, '-v', '', '-vx');
# $opt{v} == 1, @ARGV restored to ['-vx']

This allows partial processing of valid prefixes within an invalid bundle.

Empty Value Handling

Options with attached empty value (--output=) are accepted and set to the empty string. This is distinguishable from a missing argument only by context:

--output        # fails if required option (missing next argument)
--output=       # succeeds, sets output to ''
--output=file   # succeeds, sets output to 'file'

METHODS

parse

use CLI::Cmdline qw(parse);
parse(\%opt, $switches, $options)
    or die "Usage: ...\n";

Parses command-line arguments according to the switch and option specifications. Returns 1 on success, 0 on error. On error, @ARGV is restored to its original state.

Parameters
\%opt

Hashref containing option values. Switches are incremented; options take values. Missing switches default to 0, missing options to ''.

$switches

Space-separated list of switches (boolean flags). Supports -v, --verbose, aliases via |, and bundling like -vh.

$options

Space-separated list of options (require arguments). Supports -file, --input, aliases, and --file=value syntax.

Returns

1 on successful parse, 0 on error. On error, restores @ARGV to original state.

Side Effects

Modifies %opt in place. Removes processed options from @ARGV, leaving only positional arguments.

AUTHOR

Hans Harder <hans@atbas.org>

LICENSE

LICENSE AND COPYRIGHT

This software is Copyright (c) 2026 by Hans Harder.

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.

See the official Perl licensing terms: https://dev.perl.org/licenses/