This is a pure BASH implementation of getopts_long
function, which "upgrades" the built-in getopts
with support for GNU-style long options, such as:
--option
--option value
--option=value
This function is 100% compatible with the built-in getopts
. It is implemented with no external dependencies, and relies solely on BASH built-in tools to provide all of its functionality.
The implementation supports the following option syntax:
- Short options are compatible with bash’s built-in getopts:
-o
-o value
-ovalue
- Long options support GNU-like syntax:
--option
--option value
--option=value
The function is distributed via a single self-contained file:
lib/getopts_long.bash
There are a number of ways to make it available to your script, all of them come with their own advantages and disadvantages. Consider carefully all of the following and pick the method of installation that suits your needs best.
This is the recommended way of providing getopts_long
functionality to your script. To use it, clone this repository somewhere on your system:
git clone https://github.com/UrsaDK/getopts_long.git
Then update your script to source the function code:
. "__PATH_TO__/getopts_long/lib/getopts_long.bash"
This method allows you to receive any future updates and all fixes to the function by simply running git pull
within the repository. However, if you customise the function for your own needs, you might end up having to fix git merge conflicts in the future.
An alternative method of installation is to simply copy-n-paste the content of lib/getopts_long.bash
into your script.
This will make the function available to a single script and you will easily be able to customise it for your own needs. However, you will need to keep an eye on this repository and manually upgrade the function whenever an update is released.
A more advanced way to run your script with getopts_long is to mount its directory into the getopts_long docker container:
docker container run --rm --init -it -v ${YOUR_SCRIPT_DIR}:/mnt ursadk/getopts_long -l
Your project is available under /mnt
directory, and getopts_long
function can be sourced from /home/lib/getopts_long.bash
.
The container also provides the following tools:
shellcheck
- https://github.com/koalaman/shellcheckbats-core
- https://github.com/bats-core/bats-corekcov
- https://github.com/SimonKagstrom/kcov/home/bin/bats
- a custom wrapper around the above three tools that allows you to run test and generates a coverage report as long as the root directory of your project includesbin
,lib
, andtest
directories, which are used accordingly./home/bin/kcov
- a custom wrapper around thekcov
command which cleans up output reports
Please note that /home/bin
directory in pre-pended to the shell's PATH variable. As such, all files placed in /home/bin
will be executed in preference to the system tools. For example, executing kcov
runs /home/bin/kcov
not /usr/local/bin/kcov
.
The syntax for getopts_long
is the same as the syntax for the built-in getopts
:
getopts_long OPTSPEC VARNAME [ARGS...]
where:
Name | Description |
---|---|
OPTSPEC | An extended list of expected options and their arguments. |
VARNAME | A shell-variable to use for option reporting. |
ARGS | An optional list of arguments to parse. If omitted, then getopts_long will parse arguments supplied to the script. |
An OPTSPEC string tells getopts_long
which options to expect and which of them must have an argument. The syntax is very simple:
- single-character options are named first (identical to the built-in
getopts
); - long options follow the single-character options, they are named as is and are separated from each other and the single-character options by a space.
Just like with the original getopts
, when you want getopts_long
to expect an argument for an option, just place a :
(colon) after the option.
For example, given 'af: all file:'
as the OPTSPEC string, getopts_long
will recognise the following options:
-a
- a single character (short) option with no argument;-f ARG
- a single character (short) option with an argument;--all
- a multi-character (long) option with no argument;--file ARG
- a multi-character (long) option with an argument.
If the very first character of the optspec-string is a :
(colon), which would normally be nonsense because there's no option letter preceding it, getopts_long
switches to "silent error reporting mode" (See Error Reporting for more info).
In production scripts, "silent mode" is usually what you want because it allows you to handle errors yourself without being distracted by default error messages. It's also easier to handle, since the failure cases are indicated by assigning distinct characters to VARNAME
.
A good example is worth a thousand words, so here is an example of how you could use the function within a script:
#!/usr/bin/env bash
source "${PATH_TO_REPO}/lib/getopts_long.bash"
while getopts_long ':af: all file:' OPTKEY; do
case ${OPTKEY} in
'a'|'all')
echo 'all triggered'
;;
'f'|'file')
echo "file supplied -- ${OPTARG}"
;;
'?')
echo "INVALID OPTION -- ${OPTARG}" >&2
exit 1
;;
':')
echo "MISSING ARGUMENT for option -- ${OPTARG}" >&2
exit 1
;;
*)
echo "UNIMPLEMENTED OPTION -- ${OPTKEY}" >&2
exit 1
;;
esac
done
shift $(( OPTIND - 1 ))
...
In general the use of getopts_long
is identical to that of the built-in getopts
. Just like the built-in function, you need to call getopts_long
several times. Each time it will use the next positional parameter and a possible argument, if parsable, and provide it to you. The function will not change the set of positional parameters. If you want to shift them, it must be done manually:
shift $(( OPTIND - 1 ))
# now do something with $@
Just like getopts
, getopts_long
sets an exit status to FALSE when there's nothing left to parse. Thus, it's easy to use in a while-loop:
while getopts ...; do
...
done
Identical to getopts
, getopts_long
will parse options and their possible arguments. It will stop parsing on the first non-option argument (a string that doesn't begin with a hyphen (-
) that isn't an argument for any option in front of it). It will also stop parsing when it sees the --
(double-hyphen) as a stand-alone argument.
Important
To support long options and enforce identical behaviour between getopts and getopts_long when handling hyphens, getopts_long provides its own implementation for -
option. This means that the user can not use hyphen (-
) within their short option OPTSPEC, as this would override getopts_long implementation.
Like the original getopts
, getopts_long
sets the following variables:
Variable | Description |
---|---|
OPTIND | Holds the index to the next argument to be processed. This is how the function "remembers" its own status between invocations. OPTIND is initially set to 1, and needs to be re-set to 1 if you want to parse anything again with getopts. |
OPTARG | This variable is set to any argument for an option found by getopts_long . It also contains the option flag of an unknown option. |
OPTERR | (Values 0 or 1) Indicates if Bash should display error messages generated by getopts_long . The value is initialised to 1 on every shell startup - so be sure to always set it to 0 if you don't want to see annoying messages! OPTERR is not specified by POSIX for the getopts built-in utility — only for the C getopt() function in unistd.h (opterr). OPTERR is bash-specific and not supported by shells such as ksh93, mksh, zsh, or dash. |
Regarding error-reporting, there are two modes getopts_long
can run in:
- verbose mode
- silent mode
In production scripts I recommend using the silent mode, because it allows you to handle errors yourself without being distracted by default error messages. It's also easier to handle, since the failure cases are indicated by assigning distinct characters to VARNAME
.
Error type | What happens |
---|---|
invalid option | VARNAME is set to ? (question-mark) and OPTARG is unset. |
required argument not found | VARNAME is set to ? (question-mark), OPTARG is unset and an error message is printed. |
Error type | What happens |
---|---|
invalid option | VARNAME is set to ? (question-mark) and OPTARG is set to the (invalid) option character. |
required argument not found | VARNAME is set to : (colon) and OPTARG contains the option in question. |