getopt, getopts или ручной синтаксический анализ - что использовать, когда я хочу поддерживать как короткие, так и длинные варианты?

В настоящее время я пишу скрипт Bash, который имеет следующие требования:

  • он должен работать на самых разных платформах Unix /Linux.
  • он должен поддерживать как короткие, так и длинные (GNU) варианты

Я знаю, что getopts будет предпочтительным способом с точки зрения переносимости, но AFAIK не поддерживает длинные параметры.

getopt поддерживает длинные опции, но BashGuide настоятельно рекомендует:

  

Никогда не используйте getopt (1). getopt не может обрабатывать строки с пустыми аргументами или   аргументы со встроенным пробелом. Пожалуйста, забудьте, что это когда-либо   существовала.

Итак, все еще есть возможность ручного разбора. Это подвержено ошибкам, создает довольно какой-то шаблонный код, и мне нужно обрабатывать ошибки самостоятельно (думаю, getopt(s) выполняет обработку ошибок сами по себе).

Итак, какой будет предпочтительный выбор в этом случае?

28 голосов | спросил helpermethod 29 Jpm1000000pmTue, 29 Jan 2013 15:37:38 +040013 2013, 15:37:38

5 ответов


10

Если он должен быть переносимым в ряд Unices, вам придется придерживаться POSIX sh. И AFAIU там у вас просто нет выбора, кроме как переносить обработку аргументов вручную.

ответил vonbrand 29 Jpm1000000pmTue, 29 Jan 2013 17:19:24 +040013 2013, 17:19:24
23

getopt vs getopts кажется быть религиозным вопросом. Что касается аргументов против getopt в FAQ по Bash :

  • "getopt не может обрабатывать строки с пустыми аргументами", похоже, ссылается на известную проблему с аргументами optional который выглядит как getopts вообще не поддерживается (по крайней мере, от чтения help getopts для Bash 4.2.24). Из man getopt:

      

    getopt (3) может анализировать длинные параметры с необязательными аргументами, которым предоставляется пустой необязательный аргумент (но не может сделать это для коротких опций). Этот getopt (1) рассматривает необязательные аргументы, которые пусты, как если бы они не присутствовали.

Я не знаю, где «getopt не может обрабатывать аргументы [...] со встроенными пробелами», но давайте проверьте его:

  • test.sh:

    #!/usr/bin/env bash
    set -o errexit -o noclobber -o nounset -o pipefail
    params="$(getopt -o ab:c -l alpha,bravo:,charlie --name "$0" -- "[email protected]")"
    eval set -- "$params"
    
    while true
    do
        case "$1" in
            -a|--alpha)
                echo alpha
                shift
                ;;
            -b|--bravo)
                echo "bravo=$2"
                shift 2
                ;;
            -c|--charlie)
                echo charlie
                shift
                ;;
            --)
                shift
                break
                ;;
            *)
                echo "Not implemented: $1" >&2
                exit 1
                ;;
        esac
    done
    
  • пробег:

    $ ./test.sh -
    $ ./test.sh -acb '   whitespace   FTW   '
    alpha
    charlie
    bravo=   whitespace   FTW   
    $ ./test.sh -ab '' -c
    alpha
    bravo=
    charlie
    $ ./test.sh --alpha --bravo '   whitespace   FTW   ' --charlie
    alpha
    bravo=   whitespace   FTW   
    charlie
    

Похож на проверку и сопряжение со мной, но я уверен, что кто-то покажет, как я полностью неправильно понял предложение. Конечно, проблема переносимости все еще стоит; вам нужно будет решить, сколько времени стоит инвестировать в платформы с более старым или отсутствующим Bash. Мой собственный совет - использовать YAGNI и рекомендации KISS - только для тех конкретных платформ, которые, как вы знаете, будут использоваться. Переносимость кода Shell обычно идет на 100%, поскольку время разработки переходит в бесконечность.

ответил l0b0 29 Jpm1000000pmTue, 29 Jan 2013 16:25:47 +040013 2013, 16:25:47
9

Вот этот getopts_long , написанный как функция оболочки POSIX, которую вы можете встроить в свой скрипт.

Обратите внимание, что Linux getopt (из util-linux) работает корректно, если не находится в традиционном режиме и поддерживает длинные параметры, но, вероятно, это не вариант для вас, если вам нужно быть переносимым в другие Unices.

Последние версии ksh93 (getopts) и zsh (zparseopts) имеют встроенную поддержку для синтаксического анализа длинных параметров, которые могут быть для вас доступными, поскольку они доступны для большинства Unices (хотя часто они не установлены по умолчанию).

Другой вариант - использовать perl и его Getopt::Long, оба из которых должны быть доступны в большинстве Unices в настоящее время, либо путем написания всего скрипта в perl, либо просто вызвать perl просто для того, чтобы проанализировать этот параметр и передать извлеченную информацию в оболочку. Что-то вроде:

parsed_ops=$(
  perl -MGetopt::Long -le '

    @options = (
      "foo=s", "bar", "neg!"
    );

    Getopt::Long::Configure "bundling";
    $q="'\''";
    GetOptions(@options) or exit 1;
    for (map /(\w+)/, @options) {
      eval "\$o=\$opt_$_";
      $o =~ s/$q/$q\\$q$q/g;
      print "opt_$_=$q$o$q"
    }' -- "[email protected]"
) || exit
eval "$parsed_ops"
# and then use $opt_foo, $opt_bar...

Смотрите perldoc Getopt::Long, что он может сделать и как он отличается от других парсеров параметров.

ответил Stéphane Chazelas 29 Jpm1000000pmTue, 29 Jan 2013 16:24:46 +040013 2013, 16:24:46
2

В каждом обсуждении этого вопроса указывается на возможность вручную писать код разбора - только тогда вы можете быть уверены в функциональности и переносимости. Я советую вам не писать код, который вы можете сгенерировать и повторно создать с помощью простых в использовании генераторов кода с открытым кодом. Используйте Argbash , который был разработан для обеспечения окончательного ответа на вашу проблему. Это хорошо документированный генератор кода, доступный как приложение командной строки , онлайн или как изображение Docker .

Я советую против библиотек bash, некоторые из них используют getopt (что делает совершенно неспортивным), и это боль, чтобы связать гигантский нечитаемый shell blob с вашим скриптом.

ответил bubla 16 42017vEurope/Moscow11bEurope/MoscowThu, 16 Nov 2017 02:48:10 +0300 2017, 02:48:10
0

Вы можете использовать getopt для систем, которые его поддерживают, и использовать откат для систем, которые этого не делают.

Например pure-getopt реализован в чистом Bash, чтобы быть заменой для GNU getopt.

ответил Potherca 22 J000000Saturday17 2017, 15:53:51

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132