Declarative Getopt Parser for Bash
#!/bin/bash
source getopt.bash "\
getopt_default=(--help)
my-app <options...> <arguents...>
See 'man my-app' for more help.
Options:
--
b,bar Bar bar bar
q,quux= Quux quux
xxx Xxx xxx
yyy= Yyy yyy
help Show help
version Print my-app version
"
getopt "$@"
set -- "${args[@]}"
if $option_bar || $option_xxx; then
echo 'Options:'
echo "quux = $option_quux"
echo "yyy = $option_yyy"
echo 'Arguments:'
printf "- %s\n" "$@"
fi
Git has a little known but very cool getopt parser built in. The Git command is:
git rev-parse --parseopt -- <command-line-args> \
<<<"<special-format-getopt-spec-text>"
You pipe in a special format that looks like a help text describing the
command.
If you ask for --help
(or the parser fails to parse the options), it will
print a slightly reformatted version of that spec as a help message.
This library makes that Git parser even more powerful and easy to use.
-
options (or opts)
Options are the parameters like
-h
,--help
,-f <file>
,--file <file>
or--file=<file>. Note that option names are case sensitive. ie
-fand
-F` are diffent options. -
arguments (or args)
Arguments are the (zero or more) strings that (usually) follow the options.
-
long option
An option of the form
--name
or--some-thing
. Two dashes and two or more letters in the name. The names should be lowercase, start with a letter and be composed of letters, numbers and dash separators. An=
or a space may be used to separate the option from its value. ie--file=abc.txt
or--file abc.txt
. -
short option
An option of the form
-n
or-F
. A single dash and a single character for the name. The character may be a lower or upper cased letter. If the option has a value, it is separated by a space:-f abc.txt
. -
flag option
Flag options never have a value parameter. The values returned by getopt-bash are
false
(default if the option is not present) andtrue
if the option is present. -
value option
Value options always have a value parameter. This getopt-bash library returns the value specified or the empty string if it is not present in the parsed parameters.
-
dual option
A dual option can be used alone like a flag option, or with a value.
-
getopt spec
A multiline string that defines how getopt will behave. See "Spec Structure" below.
-
getopt config
The getopt library has a number of config variables you can set to get custom behavior. These variables can be set as part of the getopt spec at the top. They can also be set using normal Bash variable assignments as well.
The getopt-bash
package provides a single library file lib/getopt.bash
.
If you source getopt.bash
it provides a function called getopt
.
The getopt
function requires a getopt spec, and there are 3 ways to provide
it:
- As the
getopt_spec
config variable. - As an argument to
source getopt.bash "$spec"
. - As STDIN to the
getopt
function.
The getopt
function takes the arguments you want to parse (usually $@
) as
its arguments.
The function will (by default) set each option in a variable called
option_<option-name>
and put any arguments into an array variable called
args
.
The declared option variables will default to false
for flag options and ''
for value options.
Actually the default for a value option is an empty array, but when used in a
scalar context it will return the first value or ''
if the array is empty.
Any remaining words (not options) are placed in the array variable args
.
If a value option is specified multiple times, the option variable will be an array with all the values in the order they were used.
The variable option_count_<name>
will be set to the number of times that
option was used.
If a flag option is specified multiple times, the value will be true
.
If a dual option is specified multiple times, the first value will be used.
The option_count_<name>
variable will still be accurate though.
Here's an example:
getopt_default=--help \
getopt "$@" <<<"$spec_string"
echo "The '--foo=' option is '$option_foo'"
echo "The '--bar' option was used $option_count_bar times.
echo "The remaining arguments are '${args[*]}'"
NOTE: Bash has a builtin getopt
command that doesn't do nearly as much.
If you need to use the builtin in combination with this library, just use this
command: builtin getopt ...
for the builtin version.
The getopt
spec is a multiline string with 4 distinct sections:
-
Configuration Settings
You can put any number of lines at the top of the spec that have the form:
getopt_config_var=value
These variables can be set elsewhere or not at all. Sometimes it is nice to put them in the spec since the spec acts as an overview of how the program works.
-
Usage Prose
The first nonblank line(s) (after any config lines) is used as the
usage
in help or error messages. You should have one or more usage lines followed by a blank line. -
Documentation and Commands
The next section is where you put any free form documentation about how the command works. This is also where you typically put commands/descriptions if your program supports sub-commands. Like the
git
program has commands 'git cloneand
git checkout, etc. This section ends with a mandatory
--` line. -
Options Specification
Everything after the
--
line is where the option parsing is defined in detail. It has its own very special format described in the next section.
The overall spec layout looks like:
getopt_default=--help
getopt_prefix=opt_
my-program <opts> <args>
Description of my-program.
Options:
--
f,file= File to use
v,verbose More output
To get good at using getopt-bash you should run git help rev-parse
, search
for ^PARSEOPT
and read that entire section.
The getopt-bash library uses git rev-parse --parseopt
under the hood, but
does a bunch of extra pre-processing and post-processing to make things really
nice and simple.
The options section of the spec has these various combinations:
The options with `str` and `num` are enforced by getopt-bash
--
f,file= Description of a value option (both short and long)
help Description of a flag option (long only)
Q? Description of a dual option (short only)
color=str Value must be a string
verbose?num Value (if present) must be a number
The getopt-bash library adds additional features to the spec options syntax:
input=+ This option is required
size=+num Required and must be a number
In git rev-parse --parseopt
there are 4 flag characters: = ? * !
.
The getopt-bash library adds:
-
+
Option is required. This only makes sense for value options.
In git rev-parse
you can specify a hint word to indicate in the help text
what type of value (num
, str
, etc) is expected for a value option.
Some hints are enforced and some are suggestions.
By default, getopt-bash will enforce the following hints:
-
str
Value should be a string.
-
num
Value should be a number.
-
1..10
Value should be a number in the range 1-10.
-
file
Value should be a file that exists.
-
dir
Value should be a directory the exists.
-
path
Value should be the path of a file or directory that exists.
-
yyyy-mm-dd_HH:MM:SS
Dates and times can be specified in any layout containing the strings:
yyyy
,yy
,mm
,dd
,HH
,MM
,SS
. Underscores are used for space characters. All other characters represent themselves. -
/.../
A hint enclosed in slash characters is a Bash regex to match.
The getopt
function has sensible defaults but it is also highly configurable.
There are a number of getopt_...
variables you can use.
These variables can be set directly or by adding lines like:
getopt_default=(--help)
getopt_args=my_args
to the beginning of your spec text.
The currently available option variables are:
-
getopt_default=...
(default none)If no arguments were used, then use these. Multiple default arguments can be specified as an array:
getopt_default=(--foo --bar)
. This option is most commonly set togetopt_default=--help
(when appropriate). -
getopt_debug=<debug-opt>
(default isdebug
)Defines which option turns on
set -x
Bash debugging. The default is--debug
but that only works if you've defined thedebug
option in your spec. If you don't want to support this feature then just don't definedebug
or set this setting to an empty string.Note: This kind of debugging is extremely useful in tracking down hard to find problems.
-
getopt_prefix=word_
(default isoption_
)The prefix for the getopt option variables where values are stored.
-
getopt_args=<var>
(default isargs
)Name of the array variable to store the arguments found.
-
getopt_cmds=(...)
(default is none)A list (array) of (sub-)commands that have their own options.
See "Commands and Options" below.
-
getopt_cmds_find=...
(default is none)This config setting lets you define all commands that go into the
getopt_cmds
list in the options spec itself.If set to
true
it will take the first word from each line in the prose section that begins with spaces.Otherwise it is considered a regular expression to be applied to each line in the prose section. When a line matches, the first capture that is a word is added to the
getopt_cmds
list. -
getopt_cmds_spec=<func-name>
(default is none)A callback function that writes an options spec for the
$cmd
command to stdout.
Some programs have their own (sub-)commands that have their own set of options. The general usage looks like:
$ prog <opts> <cmd> <cmd-opts> <args>
The git
program for instance, has dozens of commands, each with their own
options.
These options are separate from the git's general options that apply to all git
commands.
The getopt.bash library supports parsing these options as well.
You just need to tell getopt
what the set of commands to look for is.
You can either set the getopt_cmds
variable to a list of commands, or use the
getopt_cmds_find
setting described above.
Each command needs to define its own options spec using the same spec format.
The way to tell getopt
which spec to use is to set getopt_cmds_spec
to the
name of a function that gets passed the command name and writes the
appropriate spec string to stdout.
When getopt
recognizes a command it puts the command name into the global
variable getopt_cmd
.
Then it calls the getopt_cmds_spec
function with that command and uses the
newly returned spec to parse the remaining options.
For a real example of a program that has many commands with their own options,
see BPAN's bpan
program.
It uses getopt.bash
to parse everything.
The source code is here.
Copyright 2020-2022. Ingy döt Net ingy@ingy.net.
getopt-bash is released under the MIT license.
See the file License for more details.