Introduction
The flag:
module provides utilities for parsing command-line flags.
This module supports two different conventions of command-line flags. The Go convention is recommended for Elvish scripts (and followed by the Elvish command itself). The alternative getopt convention is also supported, and useful for writing scripts that wrap existing programs following this convention.
Go convention
Each flag looks like -flag=value
.
For boolean flags, -flag
is equivalent to -flag=true
. For non-boolean
flags, -flag
treats the next argument as its value; in other words,
-flag value
is equivalent to -flag=value
.
Flag parsing stops before any non-flag argument, or after the flag terminator
--
.
Examples (-verbose
is a boolean flag and -port
is a non-boolean flag):
-
In
-port 10 foo -x
, theport
flag is10
, and the rest (foo -x
) are non flag arguments. -
In
-verbose 10 foo -x
, theverbose
flag istrue
, and the rest (10 foo -x
) are non-flag arguments. -
In
-port 10 -- -verbose foo
, theport
flag is10
, and the part after--
(-verbose foo
) are non-flag arguments.
Using --flag
is supported, and equivalent to -flag
.
Note: Chaining of single-letter flags is not supported: -rf
is one flag
named rf
, not equivalent to -r -f
.
Getopt convention
A flag may have either or both of the following forms:
-
A short form: a single character preceded by
-
, like-f
; -
A long form: a string preceded by
--
, like--flag
.
A flag may take:
-
No argument, like
-f
or--flag
; -
A required argument, like
-f value
,-fvalue
,--flag=value
or--flag value
; -
An optional argument, like
-f
,-fvalue
,--flag
or--flag=value
.
A short flag that takes no argument can be followed immediately by another short
flag. For example, if -r
takes no arguments, -rf
is equivalent to -r -f
.
The other short flag may be followed by more short flags (if it takes no
argument), or its argument (if it takes one). Assuming that -f
and -v
take
no arguments while -p
does, here are some examples:
-
-rfv
is equivalent to-r -f -v
. -
-rfp80
is equivalent to-r -f -p 80
.
Some aspects of the behavior can be turned on and off as needed:
-
Optionally, flag parsing stops after seeing the flag terminator
--
. -
Optionally, flag parsing stops before seeing any non-flag argument. Turning this off corresponds to the behavior of GNU’s
getopt_long
; turning it on corresponds to the behavior of BSD’sgetopt_long
. -
Optionally, only long flags are supported, and they may start with
-
. Turning this on corresponds to the behavior ofgetopt_long_only
and the Go convention.
Functions
flag:call
flag:call $fn $args &on-parse-error=$nil
Parses flags from $args
according to the signature of the
$fn
, using the Go convention, and calls $fn
.
The $fn
must be a user-defined function (i.e. not a builtin
function or external command). Each option corresponds to a flag; see
flag:parse
for how the default value affects the behavior of flags.
After parsing, the non-flag arguments are used as function arguments.
The &on-parse-error
function can be supplied to control the behavior when
$args
contains invalid flags or when the number of arguments after parsing
flags doesn’t match what $fn
expects. The function gets an argument that
describes the error condition; the argument should be treated as an opaque
value for now, but will expose more useful fields in future. If
&on-parse-error
is not supplied, such errors are raised as exceptions.
Example:
~> use flag
~> fn f {|&verbose=$false &port=(num 8000) name| put $verbose $port $name }
~> flag:call $f~ [-verbose -port 80 a.c]
▶ $true
▶ (num 80)
▶ a.c
~> flag:call $f~ [-unknown-flag] &on-parse-error={|_| echo 'bad usage' }
bad usage
~> flag:call $f~ [-verbose a b c] &on-parse-error={|_| echo 'bad usage' }
bad usage
This function is most useful when creating an Elvish script that accepts
command-line arguments. For example, if a script a.elv
contains the
following code:
use flag
fn main { |&verbose=$false &port=(num 8000) name|
...
}
flag:call $main~ $args &on-parse-error={|_|
echo 'Usage: '(src)[name]' [-verbose] [-port PORT-NUM] name'
exit 1
}
Note: This example shows how &on-parse-error
can be used to print out a
usage text, but it needs to duplicate the names of the options and arguments
accepted by main
. This is a known limitation and will hopefully be addressed
with a different API in future.
The script can be used as follows:
~> elvish a.elv -verbose -port 80 foo
...
See also flag:parse
.
flag:parse
flag:parse $args $specs
Parses flags from $args
according to the $specs
, using the Go
convention.
The $args
must be a list of strings containing the command-line arguments
to parse.
The $specs
must be a list of flag specs:
[
[flag default-value 'description of the flag']
...
]
Each flag spec consists of the name of the flag (without the leading -
),
its default value, and a description. The default value influences the how
the flag gets converted from string:
-
If it is boolean, the flag is a boolean flag (see Go convention for implications). Flag values
0
,f
,F
,false
,False
andFALSE
are converted to$false
, and1
,t
,T
,true
,True
andTRUE
to$true
. Other values are invalid. -
If it is a string, no conversion is done.
-
If it is a typed number, the flag value is converted using
num
. -
If it is a list, the flag value is split at
,
(equivalent to{|s| put [(str:split , $s)] }
). -
If it is none of the above, an exception is thrown.
On success, this command outputs two values: a map containing the value of
flags defined in $specs
(whether they appear in $args
or not), and a list
containing non-flag arguments.
Example:
~> flag:parse [-v -times 10 foo] [
[v $false 'Verbose']
[times (num 1) 'How many times']
]
▶ [×=(num 10) &v=$true]
▶ [foo]
~> flag:parse [] [
[v $false 'Verbose']
[times (num 1) 'How many times']
]
▶ [×=(num 1) &v=$false]
▶ []
See also flag:call
and flag:parse-getopt
.
flag:parse-getopt
flag:parse-getopt $args $specs &stop-after-double-dash=$true &stop-before-non-flag=$false &long-only=$false
Parses flags from $args
according to the $specs
, using the getopt
convention (see there for the semantics of the options),
and outputs the result.
The $args
must be a list of strings containing the command-line arguments
to parse.
The $specs
must be a list of flag specs:
[
[&short=f &long=flag &arg-optional=$false &arg-required=$false]
...
]
Each flag spec can contain the following:
-
The short and long form of the flag, without the leading
-
or--
. The short form, if non-empty, must be one character. At least one of&short
and&long
must be non-empty. -
Whether the flag takes an optional argument or a required argument. At most one of
&arg-optional
and&arg-required
may be true.
It is not an error for a flag spec to contain more keys.
On success, this command outputs two values: a list describing all flags
parsed from $args
, and a list containing non-flag arguments. The former
list looks like:
[
[&spec=... &arg=value &long=$false]
...
]
Each entry contains the original spec for the flag, its argument, and whether the flag appeared in its long form.
Example (some output reformatted for readability):
~> var specs = [
[&short=v &long=verbose]
[&short=p &long=port &arg-required]
]
~> flag:parse-getopt [-v -p 80 foo] $specs
▶ [[&arg='' &long=$false &spec=[&long=verbose &short=v]] [&arg=80 &long=$false &spec=[&arg-required=$true &long=port &short=p]]]
▶ [foo]
~> flag:parse-getopt [--verbose] $specs
▶ [[&arg='' &long=$true &spec=[&long=verbose &short=v]]]
▶ []
~> flag:parse-getopt [-v] [[&short=v &extra-info=foo]] # extra key in spec
▶ [[&arg='' &long=$false &spec=[&extra-info=foo &short=v]]]
▶ []
See also flag:parse
and edit:complete-getopt
.