getopt
и getopts
- разные звери, и люди, кажется, имеют некоторое недопонимание того, что они делают. getopts
- это встроенная команда для bash
для обработки параметров командной строки в цикле и назначения каждой найденной опции и значения по очереди встроенным переменным, чтобы вы могли обрабатывать их в дальнейшем. getopt
, однако, является внешней утилитой, и она на самом деле не обрабатывает ваши параметры для вас так, как, например, bash getopts
, модуль Perl Getopt
или модули Python optparse
/ argparse
. Все, что делает getopt
, - это канонизирует параметры, которые передаются, то есть преобразует их в более стандартную форму, чтобы сценарию оболочки было проще их обрабатывать. Например, приложение getopt
может преобразовать следующее:
myscript -ab infile.txt -ooutfile.txt
в это:
myscript -a -b -o outfile.txt infile.txt
Вы должны выполнить фактическую обработку самостоятельно. Вам совсем не нужно использовать getopt
, если вы накладываете различные ограничения на способ задания опций:
- ставить только один параметр на аргумент;
- все опции идут перед любыми позиционными параметрами (т. Е. Неопционные аргументы);
- для опций со значениями (например,
-o
выше) значение должно идти в качестве отдельного аргумента (после пробела).
Зачем использовать getopt
вместо getopts
? Основная причина в том, что только GNU getopt
предоставляет вам поддержку параметров командной строки с длинными именами. 1 (GNU getopt
является значением по умолчанию в Linux. Mac OS X и FreeBSD поставляются с базовыми и не очень полезный getopt
, но можно установить версию GNU; см. ниже.)
Например, вот пример использования GNU getopt
из моего скрипта с именем javawrap
:
# NOTE: This requires GNU getopt. On Mac OS X and FreeBSD, you have to install this
# separately; see below.
TEMP=`getopt -o vdm: --long verbose,debug,memory:,debugfile:,minheap:,maxheap: \
-n 'javawrap' -- "$@"`
if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi
# Note the quotes around `$TEMP': they are essential!
eval set -- "$TEMP"
VERBOSE=false
DEBUG=false
MEMORY=
DEBUGFILE=
JAVA_MISC_OPT=
while true; do
case "$1" in
-v | --verbose ) VERBOSE=true; shift ;;
-d | --debug ) DEBUG=true; shift ;;
-m | --memory ) MEMORY="$2"; shift 2 ;;
--debugfile ) DEBUGFILE="$2"; shift 2 ;;
--minheap )
JAVA_MISC_OPT="$JAVA_MISC_OPT -XX:MinHeapFreeRatio=$2"; shift 2 ;;
--maxheap )
JAVA_MISC_OPT="$JAVA_MISC_OPT -XX:MaxHeapFreeRatio=$2"; shift 2 ;;
-- ) shift; break ;;
* ) break ;;
esac
done
Это позволяет вам указать параметры, такие как --verbose -dm4096 --minh=20 --maxhe 40 --debugfi="/Users/John Johnson/debug.txt"
или аналогичные. Эффект вызова getopt
заключается в канонизации опций --verbose -d -m 4096 --minheap 20 --maxheap 40 --debugfile "/Users/John Johnson/debug.txt"
, чтобы вам было проще их обрабатывать. Кавычки вокруг "$1"
и "$2"
важны, поскольку они гарантируют, что аргументы с пробелами в них обрабатываются правильно.
Если вы удалите первые 9 строк (все до строки eval set
), код будет все еще работать ! Однако ваш код будет более разборчивым в отношении того, какие варианты он принимает: в частности, вам нужно будет указать все параметры в «канонической» форме, описанной выше. Однако с использованием getopt
вы можете группировать однобуквенные опции, использовать более короткие не двусмысленные формы длинных опций, использовать стиль --file foo.txt
или --file=foo.txt
, использовать -m 4096
или -m4096
стиль, параметры микширования и неопции в любом порядке и т. д. getopt
также выводит сообщение об ошибке, если обнаружены нераспознанные или неоднозначные опции.
ПРИМЕЧАНИЕ : На самом деле существует две совершенно разные версии getopt
, базовая getopt
и GNU getopt
, с различными функциями и различными соглашениями о вызовах. 2 Basic getopt
совершенно не работает: он не только не обрабатывает длинные опции, но и не может даже обрабатывать встроенные пробелы внутри аргументов или пустых аргументов, тогда как getopts
делает это правильно. Приведенный выше код не будет работать в основном getopt
. GNU getopt
устанавливается по умолчанию в Linux, но в Mac OS X и FreeBSD его необходимо устанавливать отдельно. В Mac OS X установите MacPorts (http://www.macports.org), а затем sudo port install getopt
, чтобы установить GNU getopt
(обычно в /opt/local/bin
), и убедитесь, что /opt/local/bin
находится на пути к вашей оболочке, а не /usr/bin
. На FreeBSD установите misc/getopt
.
Краткое руководство по изменению примера кода для вашей собственной программы: из первых нескольких строк все - это "шаблон", который должен остаться прежним, кроме строки, которая вызывает getopt
. Вам следует изменить имя программы после -n
, указать короткие параметры после -o
и длинные параметры после --long
. Поставьте двоеточие после параметров, которые принимают значение.
Наконец, если вы видите код, который имеет set
вместо eval set
, он был написан для BSD getopt
. Вы должны изменить его, чтобы использовать стиль eval set
, который отлично работает с обеими версиями getopt
, тогда как простой set
не работает с GNU getopt
.
1 На самом деле, getopts
в ksh93
поддерживает параметры с длинными именами, но эта оболочка используется не так часто, как bash
. В zsh
используйте zparseopts
, чтобы получить эту функцию.
2 Технически, "GNU getopt
" является неправильным; эта версия была написана для Linux, а не для проекта GNU. Однако оно следует всем соглашениям GNU, и термин «GNU getopt
» обычно используется (например, во FreeBSD).