Подтвердить что ты не робот

Как использовать длинные опции с встроенным Bash getopts?

Я пытаюсь проанализировать параметр -temp с помощью Bash getopts. Я вызываю свой script следующим образом:

./myscript -temp /foo/bar/someFile

Вот код, который я использую для анализа параметров.

while getopts "temp:shots:o:" option; do
    case $option in
        temp) TMPDIR="$OPTARG" ;;
        shots) NUMSHOTS="$OPTARG" ;;
        o) OUTFILE="$OPTARG" ;;
        *) usage ;;
    esac
done
shift $(($OPTIND - 1))

[ $# -lt 1 ] && usage
4b9b3361

Ответ 1

getopts может обрабатывать только короткие варианты.

В большинстве систем также есть внешняя команда getopt, но getopt не является стандартным и обычно нарушается дизайном, так как он не может безопасно обрабатывать все аргументы (аргументы с пробелами и пустыми аргументами), только GNU getopt может обрабатывать их безопасно, но только если вы используете его в GNU-специфическом способе.

Более простой выбор - не использовать, просто перебирать аргументы script с помощью while-loop и выполнять синтаксический анализ самостоятельно.

См. http://mywiki.wooledge.org/BashFAQ/035 для примера.

Ответ 2

Как объяснили другие люди, getopts не анализирует длинные варианты. Вы можете использовать getopt, но он не переносится (и он разбит на некоторой платформе...)

В качестве обходного пути вы можете реализовать цикл оболочки. Вот пример, который преобразует длинные параметры в короткие, прежде чем использовать стандартную команду getopts (это проще, на мой взгляд):

# Transform long options to short ones
for arg in "[email protected]"; do
  shift
  case "$arg" in
    "--help") set -- "[email protected]" "-h" ;;
    "--rest") set -- "[email protected]" "-r" ;;
    "--ws")   set -- "[email protected]" "-w" ;;
    *)        set -- "[email protected]" "$arg"
  esac
done

# Default behavior
rest=false; ws=false

# Parse short options
OPTIND=1
while getopts "hrw" opt
do
  case "$opt" in
    "h") print_usage; exit 0 ;;
    "r") rest=true ;;
    "w") ws=true ;;
    "?") print_usage >&2; exit 1 ;;
  esac
done
shift $(expr $OPTIND - 1) # remove options from positional parameters

Ответ 3

getopts используется процедурами оболочки для синтаксического анализа позиционных параметров только 1 символа (без длинных опций в стиле GNU (--myoption) или длинных опций XF86 (-myoption))

Ответ 4

Вы не можете использовать getopts Bash builtin для длинных параметров - по крайней мере, не имея необходимости создавать собственные функции синтаксического анализа. Вместо этого вы должны взглянуть на двоичный файл /usr/bin/getopt (предоставленный в моей системе пакетом util-linux, ваш пробег может варьироваться).

См. getopt (1) для конкретных вариантов вызова.

Ответ 5

Верно, что встроенный bash getopts анализирует короткие варианты, но вы все равно можете добавить несколько строк сценариев, чтобы makeopts обрабатывал длинные параметры.

Вот часть кода, найденного в http://www.uxora.com/unix/shell-script/22-handle-long-options-with-getopts

  #== set options ==#
SCRIPT_OPTS=':fbF:B:-:h'
typeset -A ARRAY_OPTS
ARRAY_OPTS=(
    [foo]=f
    [bar]=b
    [foobar]=F
    [barfoo]=B
    [help]=h
    [man]=h
)

  #== parse options ==#
while getopts ${SCRIPT_OPTS} OPTION ; do
    #== translate long options to short ==#
    if [[ "x$OPTION" == "x-" ]]; then
        LONG_OPTION=$OPTARG
        LONG_OPTARG=$(echo $LONG_OPTION | grep "=" | cut -d'=' -f2)
        LONG_OPTIND=-1
        [[ "x$LONG_OPTARG" = "x" ]] && LONG_OPTIND=$OPTIND || LONG_OPTION=$(echo $OPTARG | cut -d'=' -f1)
        [[ $LONG_OPTIND -ne -1 ]] && eval LONG_OPTARG="\$$LONG_OPTIND"
        OPTION=${ARRAY_OPTS[$LONG_OPTION]}
        [[ "x$OPTION" = "x" ]] &&  OPTION="?" OPTARG="-$LONG_OPTION"

        if [[ $( echo "${SCRIPT_OPTS}" | grep -c "${OPTION}:" ) -eq 1 ]]; then
            if [[ "x${LONG_OPTARG}" = "x" ]] || [[ "${LONG_OPTARG}" = -* ]]; then 
                OPTION=":" OPTARG="-$LONG_OPTION"
            else
                OPTARG="$LONG_OPTARG";
                if [[ $LONG_OPTIND -ne -1 ]]; then
                    [[ $OPTIND -le $Optnum ]] && OPTIND=$(( $OPTIND+1 ))
                    shift $OPTIND
                    OPTIND=1
                fi
            fi
        fi
    fi

    #== options follow by another option instead of argument ==#
    if [[ "x${OPTION}" != "x:" ]] && [[ "x${OPTION}" != "x?" ]] && [[ "${OPTARG}" = -* ]]; then 
        OPTARG="$OPTION" OPTION=":"
    fi

    #== manage options ==#
    case "$OPTION" in
        f  ) foo=1 bar=0                    ;;
        b  ) foo=0 bar=1                    ;;
        B  ) barfoo=${OPTARG}               ;;
        F  ) foobar=1 && foobar_name=${OPTARG} ;;
        h ) usagefull && exit 0 ;;
        : ) echo "${SCRIPT_NAME}: -$OPTARG: option requires an argument" >&2 && usage >&2 && exit 99 ;;
        ? ) echo "${SCRIPT_NAME}: -$OPTARG: unknown option" >&2 && usage >&2 && exit 99 ;;
    esac
done
shift $((${OPTIND} - 1))

Вот тест:

# Short options test
$ ./foobar_any_getopts.sh -bF "Hello world" -B 6 file1 file2
foo=0 bar=1
barfoo=6
foobar=1 foobar_name=Hello world
files=file1 file2

# Long and short options test
$ ./foobar_any_getopts.sh --bar -F Hello --barfoo 6 file1 file2
foo=0 bar=1
barfoo=6
foobar=1 foobar_name=Hello
files=file1 file2

В противном случае в недавней Korn Shell ksh93, getopts может, естественно, анализировать длинные параметры и даже отображать страницу man. (см. http://www.uxora.com/unix/shell-script/20-getopts-with-man-page-and-long-options)

Michel VONGVILAY.

Ответ 6

благодаря @mcoolive.

Я смог использовать вашу идею [email protected], чтобы преобразовать все слова и длинные варианты в одиночные варианты букв. Хотелось бы отметить всем, кто использует эту идею, что мне также пришлось включить shift $(expr $OPTIND - 1) до запуска аргументов через цикл getopts.

Совершенно другое назначение, но это хорошо работает.

# convert long word options to short word for ease of use and portability

for argu in "[email protected]"; do
  shift
  #echo "curr arg = $1"
  case "$argu" in
"-start"|"--start")
                   # param=param because no arg is required
                   set -- "[email protected]" "-s"
                   ;;
"-pb"|"--pb"|"-personalbrokers"|"--personalbrokers")
                   # pb +arg required
                   set -- "[email protected]" "-p $1"; #echo "arg=$argu"
                   ;;
"-stop"|"--stop")
                   # param=param because no arg is required 
                   set -- "[email protected]" "-S" 
                   ;;
                #  the catch all option here removes all - symbols from an
                #  argument. if an option is attempted to be passed that is
                #  invalid, getopts knows what to do...
               *)  [[ $(echo $argu | grep -E "^-") ]] && set -- "[email protected]" "${argu//-/}" || echo "no - symbol. not touching $argu" &>/dev/null
                   ;;
esac
done

#echo -e "\n final option conversions = [email protected]\n"
# remove options from positional parameters for getopts parsing
shift $(expr $OPTIND - 1)

declare -i runscript=0
# only p requires an argument hence the p:
 while getopts "sSp:" param; do
[[ "$param" == "p" ]] && [[ $(echo $OPTARG | grep -E "^-") ]] && funcUsage "order" 
#echo $param
#echo "OPTIND=$OPTIND"
case $param in
s)
       OPTARG=${OPTARG/ /}
       getoptsRan=1
       echo "$param was passed and this is it arg $OPTARG"
       arg0=start
       ;;
 p)
       OPTARG=${OPTARG/ /}
       getoptsRan=1
       echo "$param was passed and this is it arg $OPTARG"
       [[ "$OPTARG" == "all" ]] && echo -e "argument \"$OPTARG\" accepted. continuing." && (( runscript += 1 )) || usage="open"
       [[ $( echo $pbString | grep -w "$OPTARG" ) ]] && echo -e "pb $OPTARG was validated. continuing.\n" && (( runscript += 1 )) || usage="personal"
       [[ "$runscript" -lt "1" ]] && funcUsage "$usage" "$OPTARG"
       arg0=start
       ;;
S)
       OPTARG=${OPTARG/ /}
       getoptsRan=1
       echo "$param was passed and this is it arg $OPTARG"
       arg0=stop
       ;;
*)
       getoptsRan=1
       funcUsage
       echo -e "Invalid argument\n"
       ;;
esac
done
funcBuildExcludes "[email protected]"
shift $((OPTIND-1))

Ответ 7

Хотя этот вопрос был опубликован более 2 лет назад, я также нуждался в поддержке длинных вариантов XFree86; и я также хотел взять то, что мог, из getopts. Рассмотрим переключатель GCC -rdynamic. Я отмечаю r как букву флага и ожидаю dynamic внутри $OPTARG... но я хочу отклонить -r dynamic, принимая другие параметры, следующие за r.

Идея, которую я поставил ниже, основывается на наблюдении, что $OPTIND будет больше, чем в противном случае, если пространство (пробел) следует за флагом. Итак, я определяю переменную bash для хранения предыдущего значения $OPTIND, называемого $PREVOPTIND, и обновляю его в конце цикла while. Если $OPTIND 1 больше, чем $PREVOPTIND, у нас нет зазора (т.е. -rdynamic); и $GAP установлено значение false. Если вместо $OPTIND 2 больше, чем $PREVOPTIND, у нас есть пробел (например, -r dynamic), а $GAP установлен на true.

usage() { echo usage: error from $1; exit -1; }

OPTIND=1
PREVOPTIND=$OPTIND
while getopts "t:s:o:" option; do
  GAP=$((OPTIND-(PREVOPTIND+1)))
  case $option in
    t) case "${OPTARG}" in
         emp)                  # i.e. -temp
           ((GAP)) && usage "-${option} and ${OPTARG}"
           TMPDIR="$OPTARG"
           ;;
         *)
           true
           ;;
       esac
       ;;
    s) case "${OPTARG}" in
         hots)                 # i.e. -shots
           ((GAP)) && usage
           NUMSHOTS="$OPTARG"
           ;;
         *) usage "-${option} and ${OPTARG}" ;;
       esac
       ;;
    o) OUTFILE="$OPTARG" ;;
    *) usage "-${option} and ${OPTARG}" ;;
  esac
  PREVOPTIND=$OPTIND
done
shift $(($OPTIND - 1))