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