-
Notifications
You must be signed in to change notification settings - Fork 2
/
README
687 lines (543 loc) · 24 KB
/
README
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
NAME
OptArgs2 - command-line argument and option processor
VERSION
2.0.1_1 (yyyy-mm-dd)
SYNOPSIS
#!/usr/bin/env perl
use OptArgs2;
# For simple scripts use optargs()
my $args = optargs(
comment => 'script to paint things',
optargs => [
item => {
isa => 'Str',
required => 1,
comment => 'the item to paint',
},
quiet => {
isa => '--Flag',
alias => 'q',
comment => 'output nothing while working',
},
],
);
print "Painting $args->{item}\n" unless $args->{quiet};
# For complex multi-command applications
# use cmd(), subcmd() and class_optargs()
cmd 'My::app' => (
comment => 'handy work app',
optargs => [
command => {
isa => 'Str',
required => 1,
comment => 'the action to take',
},
quiet => {
isa => '--Flag',
alias => 'q',
comment => 'output nothing while working',
},
],
);
subcmd 'My::app::prepare' => (
comment => 'prepare something',
optargs => [
item => {
isa => 'Str',
required => 1,
comment => 'the item to prepare',
},
],
);
subcmd 'My::app::paint' => (
comment => 'paint something',
optargs => [
item => {
isa => 'Str',
required => 1,
comment => 'the item to paint',
},
color => {
isa => '--Str',
alias => 'c',
comment => 'your faviourite',
default => 'blue',
},
],
);
my ( $class, $opts, $file ) = class_optargs('My::app');
require $file;
$class->new($opts);
DESCRIPTION
OptArgs2 processes command line arguments, options, and subcommands
according to the following definitions:
Command
A program run from the command line to perform a task.
Arguments
Arguments are positional parameters that pass information to a
command. Arguments can be optional, but they should not be
confused with Options below.
Options
Options are non-positional parameters that pass information to a
command. They are generally not required to be present (hence
the name Option) but that is configurable. All options have a
long form prefixed by '--', and may have a single letter alias
prefixed by '-'.
Subcommands
From the users point of view a subcommand is a special argument
with its own set of arguments and options. However from a code
authoring perspective subcommands are often implemented as
stand-alone programs, called from the main script when the
appropriate command arguments are given.
Differences with Earlier Releases
OptArgs2 version 2.0.0 was a large re-write to improve the API and
code. Users upgrading from version 0.0.11 or OptArgs need to be
aware of the following:
API changes: optargs(), cmd(), subcmd()
Commands and subcommands are now explicitly defined using
"optargs()", "cmd()" and "subcmd()". The arguments to
"optargs()" have changed to match "cmd()".
Deprecated: arg(), opt(), fallback arguments
Optargs definitions must now be defined in an array reference
containing key/value pairs as shown in the synopsis. Fallback
arguments have been replaced with a new "fallthru" option.
class_optargs() no longer loads the class
Users must specifically require the class if they want to use it
afterwards.
Bool options with no default display as "--[no-]bool"
A Bool option without a default is now displayed with the
"[no-]" prefix. What this means in practise is that many of your
existing Bool options most likely would become Flag options
instead.
Simple Commands
To demonstrate the simple use case (i.e. with no subcommands) lets
put the code from the synopsis in a file called "paint" and observe
the following interactions from the shell:
$ ./paint
usage: paint ITEM [OPTIONS...]
arguments:
ITEM the item to paint *required*
options:
--help, -h print a usage message and exit
--quiet, -q output nothing while working
The "optargs()" function parses the command line (@ARGV) according
to the included declarations and returns a single HASH reference. If
the command is not called correctly then an exception is thrown
containing an automatically generated usage message as shown above.
Because OptArgs2 fully knows the valid arguments and options it can
detect a wide range of errors:
$ ./paint wall Perl is great
error: unexpected option or argument: Perl
So let's add that missing argument definition inside the optargs ref
optargs => [
...
message => {
isa => 'Str',
comment => 'the message to paint on the item',
greedy => 1,
},
],
And then check the usage again:
$ ./paint
usage: paint ITEM [MESSAGE...] [OPTIONS...]
arguments:
ITEM the item to paint, *required*
MESSAGE the message to paint on the item
options:
--help, -h print a usage message and exit
--quiet, -q output nothing while working
Note that optional arguments are surrounded by square brackets, and
that three dots (...) are postfixed to greedy arguments. A greedy
argument will swallow whatever is left on the comand line:
$ ./paint wall Perl is great
Painting on wall: "Perl is great".
Note that it probably doesn't make sense to define any more
arguments once you have a greedy argument. Let's imagine you now
want the user to be able to choose the colour if they don't like the
default. An option might make sense here, specified by a leading
'--' type:
optargs => [
...
colour => {
isa => '--Str',
default => 'blue',
comment => 'the colour to use',
},
],
This now produces the following usage output:
usage: paint ITEM [MESSAGE...] [OPTIONS...]
arguments:
ITEM the item to paint
MESSAGE the message to paint on the item
options:
--colour=STR, -c the colour to use [blue]
--help, -h print a usage message and exit
--quiet, -q output nothing while working
Multi-Level Commands
Commands with subcommands require a different coding model and
syntax which we will describe over three phases:
Definitions
Your command structure is defined using calls to the "cmd()" and
"subcmd()" functions. The first argument to both functions is
the name of the Perl class that implements the (sub-)command.
cmd 'App::demo' => (
comment => 'the demo command',
optargs => [
command => {
isa => 'SubCmd',
required => 1,
comment => 'command to run',
},
quiet => {
isa => '--Flag',
alias => 'q',
comment => 'run quietly',
},
],
);
subcmd 'App::demo::foo' => (
comment => 'demo foo',
optargs => [
action => {
isa => 'Str',
required => 1,
comment => 'command to run',
},
],
);
subcmd 'App::demo::bar' => (
comment => 'demo bar',
optargs => [
baz => {
isa => '--Counter',
comment => '+1',
},
],
);
# Command hierarchy for the above code,
# printed by using '-h' twice:
#
# demo COMMAND [OPTIONS...]
# demo foo ACTION [OPTIONS...]
# demo bar [OPTIONS...]
An argument of type 'SubCmd' is an explicit indication that
subcommands can occur in that position. The command hierarchy is
based upon the natural parent/child structure of the class
names. This definition can be done in your main script, or in
one or more separate packages or plugins, as you like.
Parsing
The "class_optargs()" function is called to parse the @ARGV
array and call the appropriate "arg()" and "opt()" definitions
as needed. It's first argument is generally the top-level
command name you used in your first "cmd()" call.
my ($class, $opts, $file) = class_optargs('App::demo');
require $file;
printf "Running %s with %s\n", $class, Dumper($opts)
unless $opts->{quiet};
The additional return value $class is the name of the actual
(sub-)command to which the $opts HASHref applies. Usage
exceptions are raised just the same as with the "optargs()"
function.
error: unknown option "--invalid"
usage: demo COMMAND [OPTIONS...]
COMMAND command to run
bar demo bar
foo demo foo
--quiet, -q run quietly
Note that options are inherited by subcommands.
Dispatch/Execution
Once you have the subcommand name and the option/argument
hashref you can either execute the action or dispatch to the
appropriate class/package as you like.
There are probably several ways to layout command classes when
you have lots of subcommands. Here is one way that seems to work
for this module's author.
lib/App/demo.pm, lib/App/demo/subcmd.pm
I typically put the actual (sub-)command implementations in
lib/App/demo.pm and lib/App/demo/subcmd.pm. App::demo itself
only needs to exists if the root command does something.
However I tend to also make App::demo the base class for all
subcommands so it is often a non-trivial piece of code.
lib/App/demo/OptArgs.pm
App::demo::OptArgs is where I put all of my command
definitions with names that match the actual implementation
modules.
package App::demo::OptArgs;
use OptArgs2;
cmd 'App::demo' => {
comment => 'the demo app',
optargs => [
# arg => 'Type, ...
# opt => '--Type, ...
],
}
The reason for keeping this separate from lib/App/demo.pm is
speed of loading. I don't want to have to load all of the
modules that App::demo itself uses just to find out that I
called the command incorrectly.
bin/demo
The command script itself is then usually fairly short:
#!/usr/bin/env perl
use OptArgs2 'class_optargs';
use App::demo::OptArgs;
my ($class, $opts, $file) = class_optargs('App::demo');
require $file;
$class->new($opts)->run;
Argument Definition
Arguments are key/hashref pairs defined inside an optargs =>
arrayref like so:
optargs => [
name => {
isa => 'Str',
comment => 'the file to parse',
default => '-',
greedy => 0,
# required => 0 | 1,
# fallthru => 0 | 1,
},
],
Any underscores in the name (i.e. the optargs "key") are replaced by
dashes (-) for presentation and command-line parsing. The following
parameters are accepted:
comment
Required. Used to generate the usage/help message.
default
The value set when the argument is not given. Conflicts with the
'required' parameter.
If this is a subroutine reference it will be called with a
hashref containg all option/argument values after parsing the
source has finished. The value to be set must be returned, and
any changes to the hashref are ignored.
greedy
If true the argument swallows the rest of the command line.
fallthru
Only relevant for SubCmd types. Normally, a "required" SubCmd
will raise an error when the given argument doesn't match any
subcommand. However, when fallthru is true the
non-subcommand-matching argument will be passed back to the
"class_optargs()" caller.
This is typically useful when you have aliases that you can
expand into real subcommands.
isa Required. Is mapped to a Getopt::Long type according to the
following table:
optargs Getopt::Long
------------------------------
'Str' '=s'
'Int' '=i'
'Num' '=f'
'ArrayRef' 's@'
'HashRef' 's%'
'SubCmd' '=s'
isa_name
When provided this parameter will be presented instead of the
generic presentation for the 'isa' parameter.
required
Set to a true value when the caller must specify this argument.
Conflicts with the 'default' parameter.
show_default
Boolean to indicate if the default value should be shown in
usage messages. Overrides the (sub-)command's "show_default"
setting.
Option Definition
Options are defined like arguments inside an optargs => arrayref
like so, the key difference being the leading "--" for the "isa"
parameter:
optargs => [
colour => {
isa => '--Str',
alias => 'c',
comment => 'the colour to paint',
default => 'blue',
show_default => 1,
},
],
Any underscores in the name (i.e. the optargs "key") are replaced by
dashes (-) for presentation and command-line parsing. The following
parameters are accepted:
alias
A single character alias.
comment
Required. Used to generate the usage/help message.
default
The value set when the option is not given. Conflicts with the
'required' parameter.
If this is a subroutine reference it will be called with a
hashref containing all option/argument values after parsing the
source has finished. The value to be set must be returned, and
any changes to the hashref are ignored.
required
Set to a true value when the caller must specify this option.
Conflicts with the 'default' parameter.
hidden
When true this option will not appear in usage messages unless
the usage message is a help request.
This is handy if you have developer-only options, or options
that are very rarely used that you don't want cluttering up your
normal usage message.
isa Required. Is mapped to a Getopt::Long type according to the
following table:
isa Getopt::Long
--- ------------
'--ArrayRef' 's@'
'--Flag' '!'
'--Bool' '!'
'--Counter' '+'
'--HashRef' 's%'
'--Int' '=i'
'--Num' '=f'
'--Str' '=s'
isa_name
When provided this parameter will be presented instead of the
generic presentation for the 'isa' parameter.
show_default
Boolean to indicate if the default value should be shown in
usage messages. Overrides the (sub-)command's "show_default"
setting.
trigger
The trigger parameter lets you define a subroutine that is
called after processing before usage exceptions are raised. This
is primarily to support --help or --version options which would
typically override usage errors.
opt version => (
isa => 'Flag',
alias => 'V',
comment => 'print version string and exit',
trigger => sub {
my ( $cmd, $value ) = @_;
die "$cmd version $VERSION\n";
}
);
The trigger subref is passed two parameters: a OptArgs2::Cmd
object and the value (if any) of the option. The OptArgs2::Cmd
object is an internal one.
Formatting of Usage Messages
Usage messages attempt to present as much information as possible to
the caller. Here is a brief overview of how the various types look
and/or change depending on things like defaults.
The presentation of Bool options in usage messages is as follows:
Name Type Default Presentation
---- ---- ------- ------------
option Bool undef --[no-]option
option Bool true --no-option
option Bool false --option
option Counter * --option
The Flag option type is like a Bool that can only be set to true or
left undefined. This makes sense for things such as "--help" or
"--version" for which you never need to see a "--no" prefix.
Name Type Default Presentation
---- ---- ------- ------------
option Flag always undef --option
Note that Flags also makes sense for "negative" options which will
only ever turn things off:
Name Type Default Presentation
---- ---- ------- ------------
no_option Flag always undef --no-option
# In Perl
no_foo => {
isa => '--Flag',
comment => 'disable the foo feature',
}
# Then later do { } unless $opts->{no_foo}
The remaining types are presented as follows:
Name Type isa_name Presentation
---- ---- -------- ------------
option ArrayRef - --option Str
option HashRef - --option Str
option Int - --option Int
option Num - --option Num
option Str - --option Str
option * XX --option XX
Defaults TO BE COMPLETED.
FUNCTIONS
The following functions are exported by default.
class_optargs( $class, [ @argv ] ) -> ($subclass, $opts, $file)
Parse @ARGV by default (or @argv when given) for the arguments
and options defined for command $class. @ARGV will first be
decoded into UTF-8 (if necessary) from whatever I18N::Langinfo
says your current locale codeset is.
Returns the following values:
$subclass
The actual subcommand name that was matched by parsing the
arguments. This may be the same as $class.
$opts
a hashref containing combined key/value pairs for options
and arguments.
$require_file
A file fragment (matching $subclass) suitable for passing to
"require".
Throws an error / usage exception object (typically
"OptArgs2::Usage::*") for missing or invalid arguments/options.
Uses OptArgs2::Pager for Help output.
As an aid for testing, if the passed in argument @argv (not
@ARGV) contains a HASH reference, the key/value combinations of
the hash will be added as options. An undefined value means a
boolean option.
cols() -> Integer
Returns the terminal column width. Only exported on request.
cmd( $class, %parameters ) -> OptArgs2::Cmd
Define a top-level command identified by $class which is
typically a Perl package name. The following parameters are
accepted:
abbrev
When set to true then subcommands can be abbreviated, up to
their shortest, unique values.
comment
A description of the command. Required.
optargs
An arrayref containing argument and option definitions. Note
that options are inherited by subcommands so you don't need
to define them again in child subcommands.
no_help
By default "cmd()" automatically adds a default '--help'
option. When used once a standard help message is displayed.
When used twice a help tree showing subcommands is
displayed. To disable the automatic help set "no_help" to a
true value.
show_color
Boolean indicating if usage messages should use ANSI
terminal color codes to highlight different elements. True
by default.
show_default
Boolean indicating if default values for options and
arguments should be shown in usage messages. Can be
overriden by sub-commands, args and opts. Off by default.
optargs( @cmd_optargs ) -> HASHref
This is a convenience function for single-level commands that:
* passes it's arguments directly to "cmd()",
* calls "class_optargs()" to parse '@ARGV' and returns the
$opts HASHref result directly.
rows() -> Integer
Returns the terminal row height. Only exported on request.
subcmd( $subclass, %parameters ) -> OptArgs2::Cmd
Defines the subcommand $subclass of a previously defined
(sub-)command.
Accepts the same parameters as "cmd()" in addition to the
following:
hidden
Hide the existence of this subcommand in non-help usage
messages. This is handy if you have developer-only or
rarely-used commands that you don't want cluttering up your
normal usage message.
usage( [$class] ) -> Str
Only exported on request, this function returns the usage string
for the command $class or the class of the calling package (.e.g
"main").
SEE ALSO
OptArgs2::Pager, OptArgs2::StatusLine, Getopt::Long
This module used to duplicate itself on CPAN as Getopt::Args2, but
as of the version 2 series that is no longer the case.
SUPPORT & DEVELOPMENT
This distribution is managed via github:
https://github.com/mlawren/p5-OptArgs/
This distribution follows the semantic versioning model:
http://semver.org/
Code is tidied up on Git commit using githook-perltidy:
http://github.com/mlawren/githook-perltidy
AUTHOR
Mark Lawrence <[email protected]>
LICENSE
Copyright 2016-2022 Mark Lawrence <[email protected]>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.