Skip to content

Proposal: add a 'mandatory' 'param' attribute - #68

Open
esaporski wants to merge 6 commits into
ko1nksm:masterfrom
esaporski:mandatory_param
Open

Proposal: add a 'mandatory' 'param' attribute#68
esaporski wants to merge 6 commits into
ko1nksm:masterfrom
esaporski:mandatory_param

Conversation

@esaporski

@esaporski esaporski commented Jan 9, 2026

Copy link
Copy Markdown

Hi,
This pull request addresses the problems cited in the issue #41.

I think it would be a good idea to have a way to flag a parameter as mandatory, so we don't need to implement checks for all necessary parameters after the argument parsing :)

Implementation

To track if the parameter was passed to the parser, I created a "control variable" to check if the flag is defined. For a parameter with a short option -p, the variable is name _p_flag_declared. For example:

parse() {
  ...
  case $1 in
  '-p')
    _p_flag_declared="true"
    [ $# -le 1 ] && set "required" "$1" && break
    ...

For a parameter with a long option --param, it would be __param_flag_declared. And for a parameter with both short and long options -p | --param, it uses the short option _p_flag_declared.

If we want to know which parameter was not defined, we cannot set an error like the other errors using something like set "mandatory" "$1" && break because if the parameter was not defined, it will never reach this set statement inside the switch case.

After the switch case is done, even if the list of arguments is still empty, we need to check for the control variables for the mandatory parameters:

[ $# -eq 0 ] &&
  [ "${_p_flag_declared:-}" = "true" ] &&
  {
    OPTIND=1
    unset OPTARG
    return 0
  }

Then, we parse the error after all the other errors:

case ${1:-} in
unknown) set "Unrecognized option: $2" "$@" ;;
noarg) set "Does not allow an argument: $2" "$@" ;;
required) set "Requires an argument: $2" "$@" ;;
pattern:*) set "Does not match the pattern (${1#*:}): $2" "$@" ;;
notcmd) set "Not a command: $2" "$@" ;;
*)
  { [ -z "${1:-}" ] && [ -z "${_p_flag_declared:-}" ] && set "Mandatory argument: -p" "mandatory" "-p"; } ||
    set "Validation error ($1): $2" "$@"
  ;;

If $1 is empty and the control variable is also empty, it means we are missing a mandatory parameter. Else, we have another validation error.

Docs

  • Added to docs/Reference.md:param:attributes: mandatory:BOOLEAN - Flag parameter as mandatory
  • Added flag to examples/advanced.sh:parser_definition
  • Added flag to examples/basic.sh:parser_definition

Tests

getoptions_base_spec.sh

When a mandatory parameter is missing:

# parser_definition() { setup ARGS; param PARAM --param mandatory:true; }
$ my_script.sh
> Mandatory argument: --param

Other errors have priority over the mandatory parameter:

# parser_definition() { setup ARGS; param PARAM --param mandatory:true; }
$ my_script.sh -x
> Unrecognized option: -x
# parser_definition() {
#   setup ARGS
#   param PARAM1 --param1 mandatory:true
#   param PARAM2 --param2 validate:'valid'
# }
# valid() { return 1; }
$ my_script.sh --param2 "invalid"
> Validation error (valid:1): --param2

For multiple mandatory parameters, it will respect the order of the parser definition:

# parser_definition() {
#   setup ARGS
#   param PARAM1 --param1
#   param PARAM2 --param2 mandatory:true
#   param PARAM3 --param3 mandatory:true
# }
$ my_script.sh --param1 "value"
> Mandatory argument: --param2

Custom error handling:

# parser_definition() {
#   setup RESTARGS error
#   param PARAM -p
#   param PARAM -q
#   param PARAM -m mandatory:true
#   param PARAM --pattern pattern:'foo | bar'
#   param VALID -v validate:'valid "$1"'
#   param ARG --arg validate:arg
#   flag  FLAG --flag
# }
# valid() { [ "$1" = "-v" ] && return 3; }
# arg() { false; }
# error() {
#   case $2 in
#     unknown) echo "custom $2: ${3:-} [$OPTARG]"; return 20 ;;
#     valid:3) echo "valid $2: ${3:-} [$OPTARG]"; return 30 ;;
#     pattern:'foo | bar') echo "pattern $2: ${3:-} [$OPTARG]"; return 40 ;;
#     mandatory) echo "custom $2: ${3:-} [$OPTARG]"; return 50 ;;
#     arg:*) echo "invalid argument [$OPTARG]"; return 1 ;;
#     noarg) echo "noarg [$OPTARG]"; return 1 ;;
#   esac
#   [ "${3:-}" = "-q" ] && echo "$1 [$OPTARG]" && return 1
#   return 0
# }
$ my_script.sh
> Mandatory argument: custom mandatory: -m []
> Status code: 50

getoptions_abbr_spec.sh

abbr enabled and mandatory parameter defined with a long option:

# parser_definition() { setup ARGS abbr:true; param PARAM --param mandatory:true; }
$ my_script.sh --p=value
> PARAM=value

abbr enabled globally but disabled for mandatory parameter and parameter defined with long and short option:

# parser_definition() { setup ARGS abbr:true; param PARAM -p --param abbr: mandatory:true; }
$ my_script.sh --p=value
> Unrecognized option: --p
# parser_definition() { setup ARGS abbr:true; param PARAM -p --param abbr: mandatory:true; }
$ my_script.sh -p value
> PARAM=value

Comment thread lib/getoptions_base.sh
Comment thread lib/getoptions_base.sh
Comment thread lib/getoptions_base.sh
Comment thread lib/getoptions_base.sh
Comment thread lib/getoptions_base.sh

@esaporski esaporski left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comments

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant