5f04e31 Updated README and source doc to reflect more usage edge-cases
~tpapastylianou pushed to ~tpapastylianou/process_optargs git
5f04e31 Updated README and source doc to reflect more usage edge-cases
~tpapastylianou pushed to ~tpapastylianou/process_optargs git
A super-simple sourceable function for processing commandline options and arguments in bash
Note: Main development for this project is done on sourcehut; however, given the visibility and social nature of github's starring system, if you appreciate this project and would like to express your appreciation and/or promote it on github, then please feel free to star it on Github, here.
Source the process_optargs
, error
, warning
, and is_in
functions from the
sourceable_functions
directory into your own scripts, and then call them as
instructed below.
See at the bottom of this file for a simple example.
process_optargs
is used inside a (caller) function (or script) to parse its
inputs into options and positional arguments.
The intended way to call process_optargs
is with "$@"
as the argument, which
effectively passes the caller function's inputs down to this function for
processing. If an error occurs during the processing of arguments, it will
return a non-zero status, which can be intercepted by the caller as appropriate.
This function expects the following variables to be declared and initialized locally in the caller function:
OPTIONS
, initialized as emptyARGS
, initialized as emptyVALID_FLAG_OPTIONS
, initialized as below.VALID_KEYVAL_OPTIONS
, initialized as belowCOMMAND_NAME
, initialized with the caller's name.The COMMAND_NAME
variable should be set to the name of the caller
function/command; its purpose is to be used internally to identify the caller by
name, in the case of error messages. If it is left empty, a default ("The
Caller") is used.
The VALID_FLAG_OPTIONS
and VALID_KEYVAL_OPTIONS
arrays is where one should
specify all flag or keyval options respectively, which are valid options for the
caller function. Individual elements of these arrays could be options in either
'short' (e.g. -o
), 'long' (e.g. --optionname
), or 'short/long' format (e.g.
-o/--optionname
). These arrays are inspected internally, and serve as a way to
confirm whether any flag and key/value options passed to the caller function
were valid options for that caller. If the caller takes no flag arguments, set
the VALID_FLAG_OPTIONS
array to the empty array (i.e. VALID_FLAG_OPTIONS=()
); and similarly for the VALID_KEYVAL_OPTIONS
array.
Note that you do not specify valid 'values' in this context, only 'keys'. Any
validation of the parsed flags and key/value pairs obtained as options via
process_optargs
should be performed by the caller after the call to
process_optargs
. An example which also demonstrates a simple validation
scenario is shown below.
The OPTIONS
and ARGS
arrays should ideally be initialized as empty. If not,
process_args will give a warning, but continue anyway. This allows the caller to
initialize them with predetermined options / args before the call to
process_optargs
. process_optargs
will then simply append any options and
arguments detected to these arrays.
After process_optargs
has exited, the OPTIONS
associative array in the
caller function will have been populated with key/value pairs; in the case of
key/value style options, the keys of the associative array will correspond to
valid 'option keys' (as specified in the VALID_KEYVAL_OPTIONS
array) in either
'short' or 'long' form (with dashes included), depending on the particular form
that what was provided to the caller.
Similarly, in the case of flag options, the keys of the OPTIONS
associative
array will correspond to valid 'option flags' in either 'short' or 'long' form
(with dashes included), depending on the particular form that was provided to
the caller, and the value always set to 'On'. In this way, you can simply check
if a flag appears as a key in the associative array to see if it has been
provided or not (if it was not provided, it will simply not exist in the
associative array; i.e. do NOT expect it to exist with a value of 'Off' -- in
other words, the value of flag-based options will always be set to 'On' but
otherwise it is something that can be safely ignored).
If an option in either short or long form appears in both the
VALID_FLAG_OPTIONS
and VALID_KEYVAL_OPTIONS
arrays, process_optargs
will
exit with an error.
Similar to OPTIONS
, after process_optargs
has exited, the ARGS
array in
the caller function will have been populated with positional arguments, i.e. the
list of "non-option" inputs passed to the function.
Note that, unlike other argument parsing tool conventions, the 0-index element
of the ARGS
array will denote the first proper argument passed to the
function, and NOT the name of the command (or function) used to call
process_optargs
. However, if you did want this behaviour for whatever reason,
since the command (or function) name used to call process_optargs
is meant to
be captured in your manually specified COMMAND_NAME
variable before the call
to process_optargs
, you can easily "append" this as the first element (i.e. at
the 0-index position) of the ARGS
array, by simply overwriting the freshly
populated ARGS
array as follows:
ARGS=( "$COMMAND_NAME" "${ARGS[@]}" )
At the point of use, when passing inputs to your caller function, you can
combine short flags together (e.g. -abc
instead of -a -b -c
), use short
keyval options with or without a space (e.g. -d1
or -d 1
), and long keyval
options using either a space or an equals sign without spaces (i.e. both --name George
and --name=George
are fine).
Notes:
As is typical of many unix tools, the special value --
causes any
subsequent inputs to be interpreted explicitly as arguments (i.e. even if
they start with dashes and are valid option names).
A single dash (i.e. -
) by itself is not special in any way, and is treated
as a normal argument (this is desired behaviour; many unix programs which
expect a filename as an argument, traditionally accept -
as a special
"filename", denoting that the input is to be read from the stdin instead.)
If a keyval option is provided in both short and long form at the same time,
process_optargs
will exit with an error, to prevent ambiguity.
If a flag-based option is provided in both short and long forms, while in
principle there is no potential for ambiguity (since they would both simply
be set to 'On'), in practice process_optargs
will also treat this as an
error, to prevent situations where you'd end up with the same option
appearing twice in the associative array (and thus requiring validation a
second time, which could waste computational time, or even cause unintended
bugs).
Contrary to the above, if a keyval or flag type option is provided two or more times, but in the same form (i.e. all short, or all long), then this is a valid invocation; the value used for that option is the one given last. This is intentional behaviour, since occasionally it is necessary to override previous options in this manner (e.g. in the case of aliases).
Since you cannot have spaces surrounding the equals sign when assigning a
value to a key/value style option, specifying --optioname=
as an input
assigns the empty string as a valid value to that option. If you want to pass
the empty string as a value using a key/value style option without an equals
sign, then you have to do so explicitly, e.g. --optionname ""
.
Similarly, with short-form options, an empty string must be passed as -i ""
explicitly; passing -i""
is invalid and won't work (since -i""
will get
simplified to -i
by the shell)
proces_optargs
depends on the error
, warning
, and is_in
functions, also
included in the sourceable_functions
directory (i.e. these need to be sourced
alongside process_optargs
in your project, for process_optargs
to work).
However, these functions are interesting in their own right, and you may well
find them useful for validating inputs in your own script. Therefore, these are
also described briefly below.
Obviously, if you prefer sourcing everything in a single line instead of sourcing each of the four functions individually, feel free to concatenate all of them to a single file and source only that in your project instead.
Given N input arguments, this function checks if the 1st input reoccurs in the remaining N-1 arguments.
The intended way to call this function is with an 'exploded' array variable as the second argument, effectively checking if the first input is a member of the array, e.g.:
Elements=( 1 3 5 7 9 )
is_in 5 "${Elements[@]}" # succeeds
is_in 6 "${Elements[@]}" # fails
A useful, 'libstderred.so' compatible function for safely echoing error messages to the stderr stream in red. E.g.:
error "This will be printed to the stderr stream in red"
Note: If you don't have libstderred installed, this will still work but errors will not be coloured in red.
Same as error
, but printed in blue instead of red. E.g.:
warning "This will be printed to the stderr stream in blue"
Depends on the error
function (i.e., it needs to be sourced alongside)
source /path/to/process_optargs
source /path/to/error
source /path/to/warning
source /path/to/is_in
function myfunction () { # The function whose options/args we want to process
# Initialise the necessary variables that will be checked / populated by process_optargs
local -A OPTIONS=()
local -a ARGS=()
local -a VALID_FLAG_OPTIONS=( -h/--help -v --version ) # note -v and --version represent separate flags here! (e.g. '-v' could be for 'verbose')
local -a VALID_KEYVAL_OPTIONS=( -r/--repetitions )
local COMMAND_NAME="myfunction"
# Perform the processing to populate the OPTIONS and ARGS arrays.
process_optargs "$@" || exit 1
# Validate parsed options and arguments
if is_in '-h' "${!OPTIONS[@]}" || is_in '--help' "${!OPTIONS[@]}"
then display_help
fi
if is_in '-r' "${!OPTIONS[@]}"; then REPS="${OPTIONS[-r]}"
elif is_in '--repetitions' "${!OPTIONS[@]}"; then REPS="${OPTIONS[--repetitions]}"
fi
if test "${#ARGS[@]}" -lt 1
then error "myfunction requires at least one non-option arguments"
exit 1
fi
# ...etc
}
# Now you can call your function with options / input arguments, e.g.:
myfunction "hello" --repetitions 5