Как использовать getopt в командной строке bash только с длинными параметрами?

13

В getoptкомандной строке bash есть команда. getoptможет использоваться с короткими опциями (такими как getopt -o axby "$@"), и может использоваться как с короткими, так и длинными опциями (такими как getopt -o axby -l long-key -- "$@"), но теперь мне нужны только длинные опции (т.е. короткие опции вообще не существуют), однако команда getopt -l long-key -- "$@"не --long-keyвариант разбора правильно. Итак, как я могу использовать getoptкоманду только с длинными параметрами? Или это невозможно или это просто ошибка getoptкоманды?

Виктор
источник
Вы помечаете для внутреннего getopts, но вы используете /usr/bin/getoptкоманду.
Anthon
@Anthon Извините, я использовал неправильный тег, но у меня недостаточно репутации, чтобы добавить еще один тег, который требует 300 репутаций. Однако я удалил неправильный тег только сейчас.
Виктор

Ответы:

15

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'
Жиль "ТАК - перестань быть злым"
источник
Помог ли ОП смешать getoptsи getoptзаразить ваш ответ? Вы начинаете с комментирования, getoptsзатем только упоминания getopt.
Anthon
@Anthon Весь мой ответ о getoptпрограмме GNU coreutils, о которой идет речь. Я исправил текст, который сказал getopts. Благодарю. getoptsдаже не делает длинных опций, так что ничего из этого не применимо к getopts.
Жиль "ТАК - перестань быть злым"
У ОП изначально был getoptsтег. Я не хотел менять ваш ответ, потому что вы, как правило, гораздо лучше меня знаете, о чем пишете :-)
Anthon
Я потерял почти час, пытаясь понять это. Вы только что сэкономили мне несколько глотков кофе. Спасибо ... давайте лучше использовать этот кофе. ☕️
dmmd
1

Не знаю, 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 анализатор - которая зависит в первую очередь от отношения evalation / назначения я пришел , чтобы оценить для aliasи $((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()определении функции оценивается, когда вызывающая оболочка создает входные документы функции здесь, но acase()никогда не вызывается и не определяется в вызывающей оболочке. Он вызывается в подоболочке, хотя, конечно, и таким образом вы можете динамически указывать интересующие вас параметры в командной строке.

Если вы передаете ему массив без разделителей, он просто заполняется acase()совпадениями для всех аргументов, начинающихся со строки --.

Функция выполняет практически всю свою обработку в подоболочке - итеративно сохраняя каждое из значений аргумента в псевдонимах, присвоенных ассоциативным именам. Когда он завершается, он печатает каждое сохраненное значение, 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. Пока он снова не найдет допустимую опцию, он будет добавлять каждый последующий аргумент в массив, который он строит, основываясь на текущем флаге. Если один и тот же параметр указан несколько раз, результаты составляются и не переопределяются. Все, что не в данном случае - или любые аргументы после игнорируемых опций - присваиваются игнорируемому массиву.

Вывод защищен от оболочки для автоматического ввода оболочкой, и поэтому:

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

И для аргументов, которые не совпадают, я бы подставил значение ignore в 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
mikeserv
источник