getopt
и getopts
это разные звери, и люди, кажется, немного не понимают, что они делают. getopts
является встроенной командой для bash
обработки параметров командной строки в цикле и назначения каждой найденной опции и значения по очереди встроенным переменным, чтобы вы могли в дальнейшем обрабатывать их. getopt
однако это внешняя служебная программа, которая фактически не обрабатывает ваши параметры так , как это делают , например, bash getopts
, Getopt
модуль Perl или модули 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
in ksh93
поддерживает параметры с длинными именами, но эта оболочка используется не так часто, как bash
. В zsh
, используйте, zparseopts
чтобы получить эту функциональность.
2 Технически, «GNU getopt
» является неправильным; эта версия была написана для Linux, а не для проекта GNU. Однако он следует всем соглашениям GNU, и термин «GNU getopt
» обычно используется (например, во FreeBSD).