How to implement subcommands with the same name in different commands #16
-
Hi @ko1nksm ... awesome library! thank you for making it! I'm taking the subcommands example at https://github.com/ko1nksm/getoptions/blob/master/examples/subcmd.sh and I have the following commands I want to implement:
How can I implement the subcommand handlers for each command with the same name? What I'm understanding is that we use # shellcheck disable=SC1083
parser_definition() {
setup REST help:usage abbr:true -- \
"Usage: ${2##*/} [global options...] [command] [options...] [arguments...]"
msg -- '' 'components command options' ''
msg -- 'Options:'
flag GLOBAL -g --global -- "global flag"
disp :usage -h --help
disp VERSION --version
msg -- '' 'Commands:'
cmd car -- "handle setup of the car"
cmd house -- "handle setup of the housec"
}
# shellcheck disable=SC1083
parser_definition_car() {
setup REST help:usage abbr:true -- \
"Usage: ${2##*/} cluster [options...] [arguments...]"
msg -- '' 'qvision state usage' ''
msg -- 'Options:'
flag GLOBAL -g --global -- "global flag"
disp :usage -h --help
disp VERSION --version
msg -- '' 'Commands:'
cmd create -- "Creates a new house storage"
}
# shellcheck disable=SC1083
parser_definition_house() {
setup REST help:usage abbr:true -- \
"Usage: ${2##*/} cluster [options...] [arguments...]"
msg -- '' 'qvision state usage' ''
msg -- 'Options:'
flag GLOBAL -g --global -- "global flag"
disp :usage -h --help
disp VERSION --version
msg -- '' 'Commands:'
cmd create -- "Creates a new house storage"
}
HOW TO HAndle the different Create?
# shellcheck disable=SC1083
parser_definition_create_car() {
setup REST help:usage abbr:true -- \
"Usage: ${2##*/} cluster [options...] [arguments...]"
msg -- '' 'create car usage' ''
msg -- 'Options:'
flag GLOBAL -g --global -- "global flag"
disp :usage -h --help
disp VERSION --version
flag CAR_MAKE -c --flag-c. -- "The make of the car "
}
# shellcheck disable=SC1083
parser_definition_create_house() {
setup REST help:usage abbr:true -- \
"Usage: ${2##*/} cluster [options...] [arguments...]"
msg -- '' 'create house usage' ''
msg -- 'Options:'
flag GLOBAL -g --global -- "global flag"
disp :usage -h --help
disp VERSION --version
flag HOUSE_ADDRESS -c --flag-c. -- "The address of the house"
} How to resolve them in the handler
if [ $# -gt 0 ]; then
cmd=$1
shift
case $cmd in
create_car)
eval "$(getoptions parser_definition_cmd1 parse "$0")"
parse "$@"
eval "set -- $REST"
echo "FLAG_A: $FLAG_A"
;;
create_house)
eval "$(getoptions parser_definition_cmd2 parse "$0")"
parse "$@"
eval "set -- $REST"
echo "FLAG_B: $FLAG_B"
;; can we specify the name of the command and the handler of the subcommand? |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 1 reply
-
If you use a subcommand in A simple implementation (without any thought) would look like this: # This is just an example.
if [ $# -gt 0 ]; then
cmd=$1
shift
case $cmd in
car)
eval "$(getoptions parser_definition_car parse "$0")"
parse "$@"
if [ $# -gt 0 ]; then
cmd=$1
shift
case $cmd in
create) echo "create car" ;;
--) # no subsubcommand
esac
fi
;;
house)
eval "$(getoptions parser_definition_house parse "$0")"
parse "$@"
if [ $# -gt 0 ]; then
cmd=$1
shift
case $cmd in
create) echo "create house" ;;
--) # subsubcommand
esac
fi
;;
--) # no subcommand
esac
fi If you want to make this even easier to maintain, it is a your task, but I will provide an example. # This is just another example.
abort() {
echo "$@" >&2
exit 1
}
create_car() {
echo "create car" "$@"
}
create_house() {
echo "create house" "$@"
}
eval "$(getoptions parser_definition parse "$0")"
parse "$@"
eval "set -- $REST"
if [ $# -eq 0 ] || [ "$1" = "--" ]; then
abort "no subcommand"
fi
object=$1
shift
eval "$(getoptions "parser_definition_${object}" parse "$0")"
parse "$@"
eval "set -- $REST"
if [ $# -eq 0 ] || [ "$1" = "--" ]; then
abort "no subsubcommand"
fi
action=$1
"${action}_${object}" "$@" The reason for requiring option parsing for each subcommand is to be able to separate the executable for each subcommand. For programs large enough to require subcommands, it is a common design to split the files for maintainability. (This is different from the design of the code above.)
Parse the first subcommand and global options in Additional notesAlso note that global_options() {
flag GLOBAL -g --global -- "global flag"
disp :usage -h --help
disp VERSION --version
}
parser_definition() {
setup REST help:usage abbr:true -- \
"Usage: ${2##*/} [global options...] [command] [options...] [arguments...]"
msg -- '' 'components command options' ''
msg -- 'Options:'
global_options
msg -- '' 'Commands:'
cmd car -- "handle setup of the car"
cmd house -- "handle setup of the housec"
}
# shellcheck disable=SC1083
parser_definition_car() {
setup REST help:usage abbr:true -- \
"Usage: ${2##*/} car [options...] [arguments...]" ""
msg -- 'Options:'
global_options
msg -- '' 'Commands:'
cmd create -- "Creates a new car storage"
}
# shellcheck disable=SC1083
parser_definition_house() {
setup REST help:usage abbr:true -- \
"Usage: ${2##*/} house [options...] [arguments...]" ""
msg -- 'Options:'
global_options
msg -- '' 'Commands:'
cmd create -- "Creates a new house storage"
} I guess this is a little different than what you were looking for, but does it answer your question? |
Beta Was this translation helpful? Give feedback.
getoptions
provides only the minimum features required for option parsing in order to reduce the size of the library. It is the task of the user to make the entire software maintainable. How you implement it depends on your design.If you use a subcommand in
getoptions
, you will need to parse arguments multiple times. This may be a peculiar design, different from other (language's) option parsing libraries.A simple implementation (without any thought) would look like this: