Command-Line Tools
More: Getopt::Long::Descriptive (options + usage), App::Cmd (git-style subcommands), CLI::Osprey (Moo-based), Term::ANSIColor (colors, core), Term::ProgressBar, Term::ReadLine, IO::Prompter, Term::Table.
Exit codes: exit 0 for success, nonzero for errors. Write errors to STDERR. Support --help. Read from STDIN when no files are given (<> does this).
#!/usr/bin/env perl
use v5.36;
use Getopt::Long qw(GetOptions :config no_ignore_case bundling);
use Pod::Usage qw(pod2usage);
my %opt = (verbose => 0, count => 10, format => 'text');
GetOptions(
'h|help' => sub { pod2usage(-verbose => 1, -exitval => 0) },
'man' => sub { pod2usage(-verbose => 2, -exitval => 0) },
'v|verbose+' => \$opt{verbose}, # incremental: -vvv
'n|count=i' => \$opt{count}, # integer
'o|output=s' => \$opt{output}, # string
'f|format=s' => \$opt{format},
'tag=s@' => \$opt{tags}, # repeatable: --tag a --tag b
'define=s%' => \$opt{define}, # key=value pairs
'dry-run!' => \$opt{dry_run}, # --dry-run / --no-dry-run
) or pod2usage(2);
pod2usage('Missing input file') unless @ARGV;
die "Invalid format\n" unless $opt{format} =~ /^(text|json|csv)$/;
for my $file (@ARGV) {
say "Processing $file (count=$opt{count})" if $opt{verbose};
}
__END__
=head1 NAME
mytool - Process files and produce reports
=head1 SYNOPSIS
mytool [options] FILE...
-n, --count N Number of records (default 10)
-o, --output FILE Output file
-f, --format FMT text|json|csv
…