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, theportflag is10, and the rest (foo -x) are non flag arguments. -
In
-verbose 10 foo -x, theverboseflag istrue, and the rest (10 foo -x) are non-flag arguments. -
In
-port 10 -- -verbose foo, theportflag 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
-for--flag; -
A required argument, like
-f value,-fvalue,--flag=valueor--flag value; -
An optional argument, like
-f,-fvalue,--flagor--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:
-
-rfvis equivalent to-r -f -v. -
-rfp80is 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_onlyand 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,FalseandFALSEare converted to$false, and1,t,T,true,TrueandTRUEto$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&shortand&longmust be non-empty. -
Whether the flag takes an optional argument or a required argument. At most one of
&arg-optionaland&arg-requiredmay 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.