Я только время от времени пишу сценарии оболочки и не пользуюсь практикой, поэтому любые отзывы приветствуются.
Используя стратегию, предложенную @Arvid Requate, мы заметили некоторые пользовательские ошибки. Пользователь, который забудет включить значение, случайно получит имя следующего параметра как значение:
./getopts_test.sh --loglevel= --toc=TRUE
приведет к тому, что значение "loglevel" будет выглядеть как "--toc = TRUE". Это может
следует избегать.
Я адаптировал некоторые идеи о проверке ошибок пользователя для CLI из http://mwiki.wooledge.org/BashFAQ/035 обсуждения ручного разбора. Я вставил проверку ошибок в обработку аргументов "-" и "-".
Затем я начал возиться с синтаксисом, поэтому любые ошибки здесь - это моя вина, а не первоначальные авторы.
Мой подход помогает пользователям, которые предпочитают вводить длинные с или без знака равенства. То есть он должен иметь тот же отклик на «--loglevel 9», что и «--loglevel = 9». В методе - / space невозможно знать наверняка, забыл ли пользователь аргумент, поэтому необходимо некоторое предположение.
- если пользователь имеет формат знака «длинный / равный» (--opt =), то пробел после = вызывает ошибку, поскольку аргумент не был предоставлен.
- если у пользователя есть длинные / пробельные аргументы (--opt), этот скрипт вызывает сбой, если ни один аргумент не следует (конец команды) или если аргумент начинается с тире)
Если вы начинаете с этого, есть интересная разница между форматами "--opt = value" и "--opt value". При знаке равенства аргумент командной строки отображается как «opt = value», а работа, выполняемая при разборе строки, отделяется при «=». Напротив, при использовании значения «--opt» имя аргумента - «opt», и мы сталкиваемся с проблемой получения следующего значения в командной строке. Вот где @Arvid Requate использовал $ {! OPTIND}, косвенную ссылку. Я до сих пор не понимаю этого, ну вообще, и комментарии в BashFAQ, кажется, предостерегают против этого стиля (http://mywiki.wooledge.org/BashFAQ/006). Кстати, я не думаю, что комментарии предыдущего автора о важности OPTIND = $ (($ OPTIND +1)) верны. Я имею в виду, я не вижу вреда, если его опустить.
В новейшей версии этого сценария флаг -v означает распечатку VERBOSE.
Сохраните его в файле с именем "cli-5.sh", сделайте исполняемым, и любой из них сработает или потерпит неудачу желаемым образом
./cli-5.sh -v --loglevel=44 --toc TRUE
./cli-5.sh -v --loglevel=44 --toc=TRUE
./cli-5.sh --loglevel 7
./cli-5.sh --loglevel=8
./cli-5.sh -l9
./cli-5.sh --toc FALSE --loglevel=77
./cli-5.sh --toc=FALSE --loglevel=77
./cli-5.sh -l99 -t yyy
./cli-5.sh -l 99 -t yyy
Вот пример выходных данных проверки ошибок в пользовательском intpu
$ ./cli-5.sh --toc --loglevel=77
ERROR: toc value must not have dash at beginning
$ ./cli-5.sh --toc= --loglevel=77
ERROR: value for toc undefined
Вы должны рассмотреть возможность включения -v, потому что он печатает внутренние компоненты OPTIND и OPTARG
#/usr/bin/env bash
## Paul Johnson
## 20171016
##
## Combines ideas from
## /252061/ispolzovanie-getopts-dlya-obrabotki-dlinnyh-i-korotkih-parametrov-komandnoi-stroki
## by @Arvid Requate, and http://mwiki.wooledge.org/BashFAQ/035
# What I don't understand yet:
# In @Arvid REquate's answer, we have
# val="${!OPTIND}"; OPTIND=$(( $OPTIND + 1 ))
# this works, but I don't understand it!
die() {
printf '%s\n' "$1" >&2
exit 1
}
printparse(){
if [ ${VERBOSE} -gt 0 ]; then
printf 'Parse: %s%s%s\n' "$1" "$2" "$3" >&2;
fi
}
showme(){
if [ ${VERBOSE} -gt 0 ]; then
printf 'VERBOSE: %s\n' "$1" >&2;
fi
}
VERBOSE=0
loglevel=0
toc="TRUE"
optspec=":vhl:t:-:"
while getopts "$optspec" OPTCHAR; do
showme "OPTARG: ${OPTARG[*]}"
showme "OPTIND: ${OPTIND[*]}"
case "${OPTCHAR}" in
-)
case "${OPTARG}" in
loglevel) #argument has no equal sign
opt=${OPTARG}
val="${!OPTIND}"
## check value. If negative, assume user forgot value
showme "OPTIND is {$OPTIND} {!OPTIND} has value \"${!OPTIND}\""
if [[ "$val" == -* ]]; then
die "ERROR: $opt value must not have dash at beginning"
fi
## OPTIND=$(( $OPTIND + 1 )) # CAUTION! no effect?
printparse "--${OPTARG}" " " "${val}"
loglevel="${val}"
shift
;;
loglevel=*) #argument has equal sign
opt=${OPTARG%=*}
val=${OPTARG#*=}
if [ "${OPTARG#*=}" ]; then
printparse "--${opt}" "=" "${val}"
loglevel="${val}"
## shift CAUTION don't shift this, fails othewise
else
die "ERROR: $opt value must be supplied"
fi
;;
toc) #argument has no equal sign
opt=${OPTARG}
val="${!OPTIND}"
## check value. If negative, assume user forgot value
showme "OPTIND is {$OPTIND} {!OPTIND} has value \"${!OPTIND}\""
if [[ "$val" == -* ]]; then
die "ERROR: $opt value must not have dash at beginning"
fi
## OPTIND=$(( $OPTIND + 1 )) #??
printparse "--${opt}" " " "${val}"
toc="${val}"
shift
;;
toc=*) #argument has equal sign
opt=${OPTARG%=*}
val=${OPTARG#*=}
if [ "${OPTARG#*=}" ]; then
toc=${val}
printparse "--$opt" " -> " "$toc"
##shift ## NO! dont shift this
else
die "ERROR: value for $opt undefined"
fi
;;
help)
echo "usage: $0 [-v] [--loglevel[=]<value>] [--toc[=]<TRUE,FALSE>]" >&2
exit 2
;;
*)
if [ "$OPTERR" = 1 ] && [ "${optspec:0:1}" != ":" ]; then
echo "Unknown option --${OPTARG}" >&2
fi
;;
esac;;
h|-\?|--help)
## must rewrite this for all of the arguments
echo "usage: $0 [-v] [--loglevel[=]<value>] [--toc[=]<TRUE,FALSE>]" >&2
exit 2
;;
l)
loglevel=${OPTARG}
printparse "-l" " " "${loglevel}"
;;
t)
toc=${OPTARG}
;;
v)
VERBOSE=1
;;
*)
if [ "$OPTERR" != 1 ] || [ "${optspec:0:1}" = ":" ]; then
echo "Non-option argument: '-${OPTARG}'" >&2
fi
;;
esac
done
echo "
After Parsing values
"
echo "loglevel $loglevel"
echo "toc $toc"