Як використовувати 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 Вибачте, я використав неправильний тег, але у мене недостатньо репутації, щоб додати ще один тег, на який потрібно 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 Уся моя відповідь стосується getoptпрограми GNU coreutils, в чому полягає питання. Я виправив текст, який сказав getopts. Дякую. getoptsнавіть не робить довгих варіантів, тому нічого з цього не стосується getopts.
Жил 'SO- перестань бути злим'

ОП спочатку мала getoptsмітку. Я не хотів змінювати вашу відповідь, тому що ви, як правило, знаєте набагато краще, ніж я, про що ви пишете :-)
Антон

Я втратив майже годину, намагаючись зрозуміти це. Ти щойно врятував мені кілька марних ковтків. Дякую ... давайте тепер краще використати цю каву. ☕️
dmmd

1

Недаремно, getoptале getoptsвбудований можна використовувати для обробки лише довгих варіантів, таких як цей:

while getopts :-: o
do  case "$o$OPTARG" in
(-longopt1) process ;;
(-longopt2) process ;;
esac; done

Звичайно, як це є, це не спрацьовує, якщо довгострокові параметри повинні мати аргументи. Це можна зробити, хоча, але, як я навчився працювати над цим. Хоча я спочатку включив його сюди, я зрозумів, що для довгих варіантів це не дуже корисно. У цьому випадку це лише скорочувало мої case (match)поля на один, передбачуваний характер. Тепер, що я знаю, це те, що він відмінно підходить для коротких варіантів - він є найбільш корисним, коли він перебирається на рядок невідомої довжини та вибирає один байт відповідно до його рядка з опціями. Але коли параметр - аргумент, з for var do case $var inкомбінацією, яку він може зробити, не так багато. Краще, я думаю, щоб це було просто.

Я підозрюю, що це правда, getoptале я не знаю про це, щоб сказати з певністю. Враховуючи наступний масив аргументів, я продемонструю свій власний маленький аналізатор аргументів - що залежить насамперед від відносин еваляції / присвоєння, які я оцінив за 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()збігами для всіх аргументів, починаючи з рядка --.

Функція виконує практично всю її обробку в підклітині - ітеративно зберігає кожне зі значень 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'

Під час проходження списку аргументів він перевіряє відповідність блоку справ. Якщо він знайде там сірник, він кидає прапор - 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

І до аргументів, які не відповідали, я би підмінив ігноровану у вищенаведеному 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.