Использование getopts для чтения одного необязательного параметра, помещенного в качестве последнего места

Я написал сценарий bash, который принимает произвольное количество параметров, и теперь я хотел бы добавить необязательный аргумент (-l) к каждому из них.

В настоящее время у меня возникают трудности с получением желаемого поведения.

Я хочу, чтобы все следующее выполнялось правильно:

./Script.sh arg1 arg2 arg3 -l opt 
./Script.sh arg1 arg2 arg3
./Script.sh arg1 arg2 arg3 arg4 -l opt
./Script.sh arg1 arg2 arg3 arg4 arg5

Проблема в том, что $OPTIND установить нельзя. Следующий цикл работает, если -l opt стоит перед первым аргументом.

while getopts ":l:" option
do
    case "$option" in
        t) 
            F_NAME=$OPTARG 
            ;;
    esac
done
shift $((OPTIND - 1))

Однако поместить необязательный -l в качестве последнего параметра является требованием. Какой самый простой способ добиться этого?


person ogs    schedule 03.03.2015    source источник
comment
Возможный дубликат Аргумент необязательной опции с getopts   -  person kenorb    schedule 30.12.2015


Ответы (2)


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

Способ управления случаем, когда необязательный параметр находится внутри команды, дан Jan Schampera в ответе на bash-hackers.org:

  if [[ $OPTARG = -* ]]; then
    ((OPTIND--))
    continue
  fi

(см.: http://wiki.bash-hackers.org/howto/getopts_tutorial глубоко на странице) Но это не работает в случае, когда опция указана в конце команды.

В этом случае это считается неправильным, поскольку параметр не задан, getopts установите для переменной opt значение ':' (двоеточие) и OPTARG для значения параметра ошибки. Таким образом, мы должны управлять случаем ':' с помощью case $OPTARG.

Допустим, мы пишем скрипт с тремя вариантами:

  • a : без параметра
  • b : с обязательным параметром
  • v : для установки подробности со значением от 0 до 2. Значение по умолчанию — 0, и предустановленное значение используется, когда скрипт вызывается с -v без параметра или с неверным значением.

Вот код:

#!/bin/bash

VERBOSITY=0 # default verbosity set to 0
PRESET_VERBOSITY=1 # preset verbosity when asked

while getopts :ab:v: opt; do
  case $opt in
    a)
      echo "manage option a"
    ;;
    b)
      echo "manage option b with value '$OPTARG'"
    ;;
    v)
      if [[ $OPTARG = -* ]]; then # Jan Schampera reply
        echo "set verbosity to PRESET (no value given, not last position)"
        VERBOSITY=$PRESET_VERBOSITY
        ((OPTIND--))
        continue
      fi

      if [[ "$OPTARG" =~ ^[0-2]$ ]]; then
        echo "set verbosity to $OPTARG (good value given)"
        VERBOSITY=$OPTARG
      else
        echo "set verbosity to PRESET (bad value given)"
        VERBOSITY=$PRESET_VERBOSITY
      fi
    ;;
    :)
      case $OPTARG in
        v)
          echo "set verbosity to PRESET (no value given, last option)"
          VERBOSITY=$PRESET_VERBOSITY
        ;;
      esac
    ;;
    \?)
      echo "WTF!"
    ;;
  esac
done

echo "**verbosity is set to $VERBOSITY**"
person FrViPofm    schedule 11.09.2015

getopts соответствует синтаксису командной строки стандарта posix, где параметры флага идут первыми. Так что его непросто использовать для нестандартных случаев.

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

Или вы можете просто интерпретировать аргумент самостоятельно.

for ((i=1; i<=$#; ++i)); do
  if [[ ${!i} == "-l" ]]; then
    ((++i))
    OPT_L=${!i}
  else
    # handle the argument (in "${!i]")
  fi
done

(Примечание: приведенное выше не выдает ошибку, если -l появляется прямо в конце списка аргументов; оно просто устанавливает значение параметра в пустую строку. Если это не подходит, что, вероятно, не так, то вставьте некоторую ошибку проверка.)

person rici    schedule 03.03.2015
comment
По сути, поскольку параметры флага идут первыми, лучше сначала передать аргумент otp ? Ваше решение, вероятно, может удовлетворить требование, но я не уверен, что часть аргумента дескриптора между else if может быть стандартизирована для удовлетворения всех возможных сценариев, нет? - person ogs; 04.03.2015
comment
@SnP: это правильно; этот небольшой фрагмент bash не предназначен для общего использования. Лично я просто использую getopts и сначала ставлю параметры опции, но getopt — это решение общего назначения (если у вас версия Gnu), поэтому я ставлю его первым в ответе. - person rici; 04.03.2015