Bash getopts: чтение $OPTARG для необязательных флагов?
Я хотел бы иметь возможность принимать как обязательные, так и необязательные флаги в моем script. Вот что я до сих пор.
#!bin/bash
while getopts ":a:b:cdef" opt; do
case $opt in
a ) APPLE="$OPTARG";;
b ) BANANA="$OPTARG";;
c ) CHERRY="$OPTARG";;
d ) DFRUIT="$OPTARG";;
e ) EGGPLANT="$OPTARG";;
f ) FIG="$OPTARG";;
\?) echo "Invalid option: -"$OPTARG"" >&2
exit 1;;
: ) echo "Option -"$OPTARG" requires an argument." >&2
exit 1;;
esac
done
echo "Apple is "$APPLE""
echo "Banana is "$BANANA""
echo "Cherry is "$CHERRY""
echo "Dfruit is "$DFRUIT""
echo "Eggplant is "$EGGPLANT""
echo "Fig is "$FIG""
Однако вывод для следующего:
bash script.sh -a apple -b banana -c cherry -d dfruit -e eggplant -f fig
... выводит это:
Apple is apple
Banana is banana
Cherry is
Dfruit is
Eggplant is
Fig is
Как вы можете видеть, необязательные флаги не вытягивают аргументы с помощью $OPTARG, как в случае с обязательными флагами. Есть ли способ читать $OPTARG на необязательных флагах, не избавляясь от аккуратной обработки ошибок ":)"?
=======================================
РЕДАКТИРОВАТЬ: Я закончил, следуя совету Гилберта ниже. Вот что я сделал:
#!/bin/bash
if [[ "$1" =~ ^((-{1,2})([Hh]$|[Hh][Ee][Ll][Pp])|)$ ]]; then
print_usage; exit 1
else
while [[ $# -gt 0 ]]; do
opt="$1"
shift;
current_arg="$1"
if [[ "$current_arg" =~ ^-{1,2}.* ]]; then
echo "WARNING: You may have left an argument blank. Double check your command."
fi
case "$opt" in
"-a"|"--apple" ) APPLE="$1"; shift;;
"-b"|"--banana" ) BANANA="$1"; shift;;
"-c"|"--cherry" ) CHERRY="$1"; shift;;
"-d"|"--dfruit" ) DFRUIT="$1"; shift;;
"-e"|"--eggplant" ) EGGPLANT="$1"; shift;;
"-f"|"--fig" ) FIG="$1"; shift;;
* ) echo "ERROR: Invalid option: \""$opt"\"" >&2
exit 1;;
esac
done
fi
if [[ "$APPLE" == "" || "$BANANA" == "" ]]; then
echo "ERROR: Options -a and -b require arguments." >&2
exit 1
fi
Большое спасибо, всем. До сих пор это прекрасно работает.
Ответы
Ответ 1
Большинство оболочек getopts раздражали меня в течение длительного времени, в том числе отсутствие поддержки необязательных аргументов.
Но если вы готовы использовать аргументы стиля "--posix", посетите аргумент аргумента bash аргументов для аргументов в [email protected]
Ответ 2
:
означает "принимает аргумент", а не "обязательный аргумент". То есть символ опции, за которым не следует :
, означает параметр стиля флага (без аргумента), тогда как символ опции, за которым следует :
, означает параметр с аргументом.
Таким образом, вы, вероятно, хотите
getopts "a:b:c:d:e:f:" opt
Если вы хотите "обязательные" параметры (немного оксюморона), вы можете проверить после разбора аргумента, что все обязательные значения параметров были установлены.
Ответ 3
Это непросто... Любые опциональные аргументы опции должны быть необходимы, насколько известно getopts
. Конечно, необязательный аргумент должен быть частью того же аргумента в script как опция, с которой он идет. В противном случае опция -f
с необязательным аргументом и опция -a
с требуемым аргументом могут запутаться:
# Is -a an option or an argument?
./script.sh -f -a foo
# -a is definitely an argument
./script.sh -f-a foo
Единственный способ сделать это - проверить, является ли параметр и его аргумент одним аргументом в script. Если это так, OPTARG является аргументом опции. В противном случае OPTIND должен быть уменьшен на единицу. Конечно, теперь у опции есть аргумент, означающий, что символ будет найден, когда в опции отсутствует аргумент. Просто используйте другой case
, чтобы определить, нужны ли какие-либо параметры:
while getopts ":a:b:c:d:e:f:" opt; do
case $opt in
a) APPLE="$OPTARG";;
b) BANANA="$OPTARG";;
c|d|e|f)
if test "$OPTARG" = "$(eval echo '$'$((OPTIND - 1)))"; then
OPTIND=$((OPTIND - 1))
else
case $opt in
c) CHERRY="$OPTARG";;
d) DFRUIT="$OPTARG";;
...
esac
fi ;;
\?) ... ;;
:)
case "$OPTARG" in
c|d|e|f) ;; # Ignore missing arguments
*) echo "option requires an argument -- $OPTARG" >&2 ;;
esac ;;
esac
done
Это сработало для меня до сих пор.
Ответ 4
Для bash это мой любимый способ разбора/поддержки cli args. Я использовал getopts, и было слишком сложно, что он не поддерживал бы длинные варианты. Мне нравится, как это работает в противном случае - особенно для встроенных функций.
usage()
{
echo "usage: $0 -OPT1 <opt1_arg> -OPT2"
}
while [ "`echo $1 | cut -c1`" = "-" ]
do
case "$1" in
-OPT1)
OPT1_ARGV=$2
OPT1_BOOL=1
shift 2
;;
-OPT2)
OPT2_BOOL=1
shift 1
;;
*)
usage
exit 1
;;
esac
done
Короткий, простой. Лучший друг инженера!
Я думаю, что это может быть изменено для поддержки параметров "-", а также...
Приветствия =)
Ответ 5
Понимание bash
getopts
На странице руководства bash
(со ссылкой на руководство версии 4.1) для getopts
указано:
getopts optstring name
[args]
getopts
используется скриптами оболочки для анализа позиционных параметров. optstring содержит символы опций, подлежащие распознаванию; если за персонажем следует двоеточие, ожидается, что параметр будет иметь аргумент, который должен быть отделен от него по белому пространству. Двоеточие (':) и вопросительный знак (??) Могут не быть используется как символ опции. Каждый раз, когда он вызывается, getopts
помещает следующий параметр в имени переменной оболочки, инициализируя имя, если оно не существует, и индекс следующего аргумента, который будет обработан в переменной OPTIND
. OPTIND
инициализируется до 1 каждый раз, когда вызывается оболочка или оболочка script. Когда опция требует аргумента, getopts
помещает этот аргумент в переменную OPTARG
. Оболочка не reset OPTIND
автоматически; он должен быть вручную reset между несколькими вызовами getopts
в пределах одного вызова оболочки, если необходимо использовать новый набор параметров.
Когда встречается конец опций, getopts
выходит с возвращаемым значением больше нуля. OPTIND
устанавливается в индекс первого аргумента без опции, и имя установлено на '?.
getopts
обычно анализирует позиционные параметры, но если больше аргументов заданный в args, getopts
вместо этого анализирует их.
getopts
может сообщать об ошибках двумя способами. Если первый символ optstring является двоеточие, тихая отчетность об ошибках. В диагностических сообщениях нормальной работы печатаются, когда встречаются недопустимые параметры или отсутствующие аргументы параметра. Если для переменной OPTERR
установлено значение 0, сообщения об ошибках не будут отображаться, даже если первый символ optstring не является двоеточием.
Если отображается недопустимая опция, getopts
places '? на имя и, если не молчать, печатает сообщение об ошибке и unsets OPTARG
. Если getopts
не работает, параметр найденный символ помещается в OPTARG
, и диагностическое сообщение не печатается. Если требуемый аргумент не найден, а getopts
не является тихим, знак вопроса ('?) Помещается в имя, OPTARG
не отображается, и выводится диагностическое сообщение. Если getopts
молчат, тогда двоеточие (':) помещается в имя и OPTARG
устанавливается на найден символ опции.
Обратите внимание, что:
Я предполагаю, что вы после функциональности сродни:
script -ffilename
script -f
где флаг f
(-f
) необязательно принимает аргумент. Это не поддерживается командой bash
getopts
. Функция POSIX getopt()
едва поддерживает эту нотацию. Фактически, только последний параметр в командной строке может иметь необязательный аргумент в POSIX.
Каковы альтернативы?
В части проконсультируйтесь с с помощью getopts
в bash
оболочке script, чтобы получить длинные и короткие параметры командной строки.
Программа GNU getopt
(единственная!) - это сложное beastie, которое поддерживает длинные и короткие опции и поддерживает необязательные аргументы для длинных опций (и использует GNU getopt(3)
. Отслеживание его источника интересно, ссылка на странице на die.net неверна, вы найдете это в подкаталоге в ftp://ftp.kernel.org/pub/linux/utils/util-linux (без -ng
). Я не отслеживал местоположение в http://www.gnu.org/ или http://www.fsf.org/, который содержит его.