
getopt
В командной строке bash есть команда, getopt
которую можно использовать с короткими параметрами (например, getopt -o axby "$@"
), а также можно использовать как с короткими, так и с длинными параметрами (например, getopt -o axby -l long-key -- "$@"
), но сейчас мне нужнотолькодлинные опции (т.е. короткие опции вообще не существуют), однако команда getopt -l long-key -- "$@"
не разбирает --long-key
опцию правильно. Так как я могу использовать getopt
команду столькодлинные опции? Или это невозможно или это просто баг команды getopt
?
решение1
getopt
прекрасно справляется с отсутствием коротких опций. Но вам нужно сообщить ему, что у вас нет коротких опций. Это причуда в синтаксисе — из руководства:
Если в первой части не найдено ни
-o
одного--options
параметра, то в качестве короткой строки параметров используется первый параметр второй части.
Вот что происходит в вашем тесте: getopt -l long-key -- --long-key foo
обрабатывает --long-key
как список вариантов -egklnoy
и foo
как единственный аргумент. Используйте
getopt -o '' -l long-key -- "$@"
например
$ getopt -l long-key -o '' -- --long-key foo
--long-key -- 'foo'
$ getopt -l long-key -o '' -- --long-key --not-recognized -n foo
getopt: unrecognized option '--not-recognized'
getopt: invalid option -- 'n'
--long-key -- 'foo'
решение2
Не знаю getopt
, но getopts
встроенную функцию можно использовать только для обработки длинных опций, например:
while getopts :-: o
do case "$o$OPTARG" in
(-longopt1) process ;;
(-longopt2) process ;;
esac; done
Конечно, как есть, это не работает, если длинные опции должны иметь аргументы. Это можно сделать, однако, как я узнал, работая над этим. Хотя я изначально включил это сюда, я понял, что для длинных опций это не имеет большой пользы. В этом случае это только сокращало мои case
(match)
поля на один предсказуемый символ. Теперь, что я знаю, это то, что это отлично подходит для коротких опций - это наиболее полезно, когда он проходит по строке неизвестной длины и выбирает отдельные байты в соответствии с его опцией-строкой. Но когда опцияявляетсяarg, вы не делаете много с for var do case $var in
комбинацией, которую она могла бы сделать. Лучше, я думаю, сделать ее простой.
Я подозреваю, что то же самое относится и к , getopt
но я недостаточно знаю об этом, чтобы сказать с уверенностью. Учитывая следующий массив arg, я продемонстрирую свой собственный небольшой парсер arg, который в первую очередь зависит от отношения оценки/присваивания, которое я оценил для alias
and $((shell=math))
.
set -- this is ignored by default --lopt1 -s 'some '\''
args' here --ignored and these are ignored \
--alsoignored andthis --lopt2 'and
some "`more' --lopt1 and just a few more
Это строка аргумента, с которой я буду работать. Теперь:
aopts() { env - sh -s -- "$@"
} <<OPTCASE 3<<\OPTSCRIPT
acase() case "\$a" in $(fmt='
(%s) f=%s; aset "?$(($f)):";;\n'
for a do case "$a" in (--) break;;
(--*[!_[:alnum:]]*) continue;;
(--*) printf "$fmt" "$a" "${a#--}";;
esac;done;printf "$fmt" '--*' ignored)
(*) aset "" "\$a";;esac
shift "$((SHIFT$$))"; f=ignored; exec <&3
OPTCASE
aset() { alias "$f=$(($f${1:-=$(($f))+}1))"
[ -n "${2+?}" ] && alias "${f}_$(($f))=$2"; }
for a do acase; done; alias
#END
OPTSCRIPT
Это обрабатывает массив arg одним из двух способов в зависимости от того, передаете ли вы ему один или два набора аргументов, разделенных разделителем --
. В обоих случаях это применяется к последовательностям обработки массива arg.
Если вы назовете это так:
: $((SHIFT$$=3)); aopts --lopt1 --lopt2 -- "$@"
Первым делом необходимо записать acase()
функцию, которая будет выглядеть следующим образом:
acase() case "$a" in
(--lopt1) f=lopt1; aset "?$(($f)):";;
(--lopt2) f=lopt2; aset "?$(($f)):";;
(--*) f=ignored; aset "?$(($f)):";;
(*) aset "" "$a";;esac
И рядом с shift 3
. Подстановка команды в acase()
определении функции оценивается, когда вызывающая оболочка создает входные here-документы функции, но acase()
никогда не вызывается и не определяется в вызывающей оболочке. Она вызывается в подоболочке, конечно, и таким образом вы можете динамически указывать интересующие вас опции в командной строке.
Если вы передадите ему неразделенный массив, он просто заполнит acase()
его совпадениями для всех аргументов, начинающихся со строки --
.
Функция выполняет практически всю свою обработку в подоболочке — итеративно сохраняя каждое из значений arg в псевдонимы, назначенные с ассоциативными именами. Когда она завершается, она выводит каждое сохраненное с помощью него значение alias
— что POSIX-определено для печати всех сохраненных значений, заключенных в кавычки таким образом, что их значения можно повторно ввести в оболочку. Поэтому, когда я делаю...
aopts --lopt1 --lopt2 -- "$@"
Его вывод выглядит так:
...ignored...
lopt1='8'
lopt1_1='-s'
lopt1_2='some '\'' args'
lopt1_3='here'
lopt1_4='and'
lopt1_5='just'
lopt1_6='a'
lopt1_7='few'
lopt1_8='more'
lopt2='1'
lopt2_1='and
some "`more'
Проходя по списку аргументов, он проверяет блок case на предмет совпадения. Если он находит совпадение, он выдает флаг - f=optname
. Пока он снова не найдет допустимый параметр, он будет добавлять каждый последующий аргумент в массив, который он строит на основе текущего флага. Если один и тот же параметр указан несколько раз, результаты объединяются и не переопределяются. Все, что не в case, или любые аргументы, следующие за игнорируемыми параметрами, присваиваютсяпроигнорированомножество.
Вывод автоматически безопасен для ввода в оболочку самой оболочкой, поэтому:
eval "$(: $((SHIFT$$=3));aopts --lopt1 --lopt2 -- "$@")"
...должно быть совершенно безопасно. Если по какой-либо причинене являетсябезопасно, то вам, вероятно, следует отправить отчет об ошибке вашему разработчику оболочки.
Он назначает два типа значений псевдонима для каждого совпадения. Во-первых, он устанавливает флаг — это происходит независимо от того, предшествует ли опция несовпадающим аргументам. Таким образом, любое вхождение --flag
в списке аргументов вызовет flag=1
. Это не объединяет — --flag --flag --flag
просто получает flag=1
. Это значениеделаетинкремент, хотя - для любых аргументов, которые могут следовать за ним. Его можно использовать как ключ индекса. После выполнения вышеописанного eval
я могу сделать:
printf %s\\n "$lopt1" "$lopt2"
...получить...
8
1
И так:
for o in lopt1 lopt2
do list= i=0; echo "$o = $(($o))"
while [ "$((i=$i+1))" -le "$(($o))" ]
do list="$list $o $i \"\${${o}_$i}\" "
done; eval "printf '%s[%02d] = %s\n' $list"; done
ВЫХОД
lopt1 = 8
lopt1[01] = -s
lopt1[02] = some ' args
lopt1[03] = here
lopt1[04] = and
lopt1[05] = just
lopt1[06] = a
lopt1[07] = few
lopt1[08] = more
lopt2 = 1
lopt2[01] = and
some "`more
А для аргументов, которые не совпали, я бы заменилпроигнорированов поле выше for ... in
, чтобы получить:
ignored = 10
ignored[01] = this
ignored[02] = is
ignored[03] = ignored
ignored[04] = by
ignored[05] = default
ignored[06] = and
ignored[07] = these
ignored[08] = are
ignored[09] = ignored
ignored[10] = andthis