Я написал сценарий в Bash, который служит в качестве шаблона для нескольких мониторов. Я выбираю getopt, чтобы иметь возможность использовать длинные опции в CLI. Однако у меня есть некоторые проблемы с его реализацией.Getopt не разбирается в bash
Весь сценарий намного дольше, но это соответствующая часть:
#!/bin/bash
#
# FUNCTION
# main
# DESCRIPTION
# Main function. Everything will be called from here
# ARGS
# Nothing
#
# RETURN CODE
# Nothing
#
# Main function. Everything will be called from here
main() {
# Parse the options and arguments
parse_options "${@}"
# Check if the interval is set to a valid number
check_interval
}
#
# FUNCTION
# check_interval
# DESCRIPTION
# Checks if a number is valid
# ARGS
# 1: number to be checked
# RETURN CODE
# 0: valid
# 1: invalid
#
check_interval() {
# We don't have to worry if interval is set at all, because getopt is already doing this
if (! check_number_pos ${arginterval}); then
echo "Error: invalid interval: ${arginterval}"
show_usage
exit 2
fi
}
#
# FUNCTION
# show_usage
# DESCRIPTION
# This is the Usage section. We a showing the Usage according to docopt standards
# ARGS
# Nothing
# RETURN CODE
# Nothing
#
show_usage() {
echo "Usage:" >&2
echo " ${THIS_SCRIPT_NAME} -i|--interval=<interval in s> [-r | --random [--randomwait=<wait in s>]] [-v|--verbose] [-d|--debug] [--colors]" >&2
echo " ${THIS_SCRIPT_NAME} [-h|--help]" >&2
}
#
# FUNCTION
# check_number_pos
# DESCRIPTION
# Checks if a number is valid and positive
# ARGS
# 1: number to be checked
#
# RETURN CODE
# 0: valid and positive
# 1: invalid or negative
#
check_number_pos() {
local returnval
if [[ "${1}" =~ ^[0-9]+$ ]]; then
returnval=0
else
returnval=1
fi
return ${returnval}
}
#
# FUNCTION
# parse_options
# DESCRIPTION
# Parse options from command line
# ARGS
# @: Arguments and options as given at CLI
# RETURN CODE
# Nothing
#
parse_options() {
# Use getopt(1) to parse options according to POSIX. If it fails, an error is shown, and we're showing the Usage and exit
# Add new options here and also in the case-statement below.
# Short options must be added in the 'options'-section
# Long options must be added in the 'longoptions'-section
# All short options must have a long equivalent
# The --name is set so the error-output will not show 'getopt'-errors but neat <application name>-errors
# Options and longoptions have the following format:
# <letter> Option without argument
# <letter>: Option with mandarory argument
# <letter>:: Option with optional argument <- this is broken for short options and long options without '='. Don't use it!
local -r GETOPT=$(getopt --name ${0} --options hrvdi: --longoptions help,random,verbose,debug,colors,randomwait:,interval: -- "${@}")
if [ ${?} != 0 ]; then
echo "Error: Error while getting arguments"
show_usage
exit 127;
fi
# No options or arguments given. Show Usage.
if [[ "${GETOPT}" == " --" ]]; then
show_usage
exit 127;
fi
# Evaluate GETOPT. We need this to have the quotes in the output of getopt(1) interpreted.
eval set -- "${GETOPT}"
# Walk through all the options. Don't put too much code in here, just point to a function or set a variable.
# Please note, all new options need to be added here but also in the GETOPT line above.
# Note: shift removes the first value from the string, so the option itself will be removed from the GETOPT-string, and the argument is available in $1
# After using an argument, please shift again, so the next option will be the first value in GETOPT
while true;
do
case "${1}" in
-i|--interval)
shift
arginterval=${1}
shift
;;
-r|--random)
shift
flagrandom=1
;;
--randomwait)
shift
flagrandom=1
argrandom=${1}
shift
;;
-v|-d|--verbose|--debug)
flagdebug=1
shift
;;
--colors)
flagcolors=1
shift
;;
-h|--help)
#show_help
exit 0
;;
--)
shift
break
;;
-*)
echo "Error: unrecognized option ${1}"
show_usage
exit 127
;;
*)
show_usage
exit 127
;;
esac
done
}
#Call main function after all
main "${@}"
Теперь, когда я звоню скрипт правильного пути, все идет гладко:
$ ./test1.sh -i 10
Когда я забыть аргумент, он также делает то, что я хочу:
$ ./test1.sh -i
./test1.sh: option requires an argument -- 'i'
Usage:
-i|--interval=<interval in s> [-r | --random [--randomwait=<wait in s>]] [-v|--verbose] [-d|--debug] [--colors]
[-h|--help]
Но когда я просто забываю аргумент и добавляю другой, он не:
$ ./test1.sh -i --colors
Error: invalid interval: --colors
Usage:
-i|--interval=<interval in s> [-r | --random [--randomwait=<wait in s>]] [-v|--verbose] [-d|--debug] [--colors]
[-h|--help]
Это происходит потому, что я проверяю, если интервал является целым числом, но и для других целей, это опасная вещь Как я могу изменить случай, поэтому он не будет читать параметры в качестве аргументов ? До сих пор getopt не очень хорошо меня обслуживал, потому что я также столкнулся с другой ошибкой/функцией: «необязательный аргумент» (: :) не работает так, как я ожидал, поскольку он работает только при использовании с параметром «=» между опцией и аргумент.
Версии:
$ getopt -V
getopt (enhanced) 1.1.4
$ bash --version
GNU bash, version 4.1.2(1)-release (x86_64-redhat-linux-gnu)
Действительно; аналогично, 'getup' требует, чтобы аргумент _optional_ option был привязан непосредственно к опции_ - без разделителя в короткой форме (например,' -i10'), с '=' в качестве разделителя в длинной форме (например, '- -interval = 10') - потому что это единственный _умножественный способ сделать это. – mklement0
Хорошо, может быть, я пытаюсь заставить думать слишком глупо, но я ожидал, что getopt поймет, что --colors - это «вариант», потому что я ему сказал. Я добавлю еще несколько проверок скрипту, чтобы предотвратить ошибочный ввод. –
'--colors' может быть вариантом, но нельзя сказать' getopt', что он также не может быть допустимым аргументом '-i'. – chepner