Использование getopts внутри функции Bash

Я хотел бы использовать getopts внутри функции, которую я определил в своем .bash_profile. Идея в том, что я хотел бы передать этой функции несколько флагов, чтобы изменить ее поведение.

Вот код:

function t() {
    echo $*
    getopts "a:" OPTION
    echo $OPTION
    echo $OPTARG
}

Когда я вызываю его так:

t -a bc

Я получаю этот вывод:

-a bc
?
 

Что не так? Я хотел бы получить значение bc без ручного сдвига и разбора. Как правильно использовать getopts внутри функции?

РЕДАКТИРОВАТЬ: исправил мой фрагмент кода, чтобы попробовать $OPTARG, но безрезультатно

РЕДАКТИРОВАТЬ № 2: Хорошо, получается, что код в порядке, моя оболочка как-то испортилась. Открытие нового окна решило это. Значение arg действительно было в $OPTARG.


person Magnus    schedule 20.05.2013    source источник


Ответы (3)


Как указывает @Ansgar, аргумент вашего параметра хранится в ${OPTARG}, но это не единственное, на что следует обратить внимание при использовании getopts внутри функции. Вам также необходимо убедиться, что ${OPTIND} является локальным для функции, либо отключив его, либо объявив local, иначе вы столкнетесь с непредвиденным поведением при многократном вызове функции.

t.sh:

#!/bin/bash

foo()
{
    foo_usage() { echo "foo: [-a <arg>]" 1>&2; exit; }

    local OPTIND o a
    while getopts ":a:" o; do
        case "${o}" in
            a)
                a="${OPTARG}"
                ;;
            *)
                foo_usage
                ;;
        esac
    done
    shift $((OPTIND-1))

    echo "a: [${a}], non-option arguments: $*"
}

foo
foo -a bc bar quux
foo -x

Пример запуска:

$ ./t.sh
a: [], non-option arguments:
a: [bc], non-option arguments: bar quux
foo: [-a <arg>]

Если вы закомментируете # local OPTIND, вместо этого вы получите следующее:

$ ./t.sh
a: [], non-option arguments:
a: [bc], non-option arguments: bar quux
a: [bc], non-option arguments:

Кроме этого, его использование такое же, как и при использовании вне функции.

person Adrian Frühwirth    schedule 20.05.2013
comment
1.) 1 в 1>&2 не требуется. 2.) Вы не определили a, o и OPTARG как локальные. 3.) exit не выйдет из скрипта, а только из под-оболочки. Для выхода из скрипта необходимо set -e во внешней оболочке и exit 1 во вспомогательной оболочке. Пример не вызывает проблемы, но MSG=$(foo ...) ее вызовет. - person ceving; 12.10.2014
comment
@ceving 1) Это вопрос стиля кодирования, но нет, это не обязательно согласно определению языка. 2) Согласен, те должны быть местными. 3) Как вы сказали, выход будет выходить из скрипта в моем примере. Конечно, exit не выйдет из подоболочек, но это не является конкретной проблемой для этого вопроса. Вам не нужно использовать set -e, вам просто нужно убедиться, что вы поймали ошибку, и MSG=$(foo ...) || die работает так же хорошо. set -e — это одно из решений проблемы, но оно не защищает от идиотов, и я, как и многие другие, не рекомендую его использовать. - person Adrian Frühwirth; 13.10.2014
comment
Просто указываю, что даже если вы не используете OPTIND в функции (например, с shift;), ее все равно необходимо локализовать, чтобы иметь предсказуемое поведение. - person ; 23.08.2016
comment
@ceving, вы можете посмотреть здесь, stackoverflow.com/questions/61566331/ - я добавил local, но не помогло - person Herdsman; 03.05.2020

Вот простой пример использования getopts в функции оболочки:

#!/usr/bin/env bash
t() {
  local OPTIND
  getopts "a:" OPTION
  echo Input: $*, OPTION: $OPTION, OPTARG: $OPTARG
}
t "$@"
t -a foo

Выход:

$ ./test.sh -a bc
Input: -a bc, OPTION: a, OPTARG: bc
Input: -a foo, OPTION: a, OPTARG: foo

Как указал @Adrian, необходимо установить local OPTIND (или OPTIND=1), поскольку оболочка не сбрасывает OPTIND автоматически между несколько вызовов getopts (man bash).

Базовый синтаксис для getopts:

getopts OPTSTRING VARNAME [ARGS...]

и по умолчанию отсутствие указания аргументов эквивалентно явному вызову с помощью «$@», что означает: getopts "a:" opts "$@".

В случае проблем, это используемые переменные для getopts для проверки:

  • OPTIND - индекс следующего обрабатываемого аргумента,
  • OPTARG - переменной присваивается любой аргумент для опции, найденной getopts,
  • OPTERR (не POSIX) — установите значение 0 или 1, чтобы указать, должен ли Bash отображать сообщения об ошибках, сгенерированные getopts.

Подробнее см.: Небольшой учебник по getopts на The Bash Hackers Wiki.

person kenorb    schedule 29.12.2015
comment
Очень продуманно изложенный ответ. Я не знаю, работает ли это, но отличная работа. - person Drew; 30.12.2015
comment
@kenorb, вы можете посмотреть здесь -› stackoverflow.com/questions/61566331/, добавил local, но не помогло - person Herdsman; 03.05.2020

Аргумент хранится в переменной $OPTARG.

function t() {
  echo $*
  getopts "a:" OPTION
  echo $OPTION
  echo $OPTARG
}

Выход:

$ t -a bc
-a bc
a
bc
person Ansgar Wiechers    schedule 20.05.2013
comment
Извините, я неправильно вставил свой фрагмент кода... Я также повторяю $OPTARG, это 3-я строка, которая пуста. Любые другие идеи? - person Magnus; 21.05.2013
comment
@Magnus Возможно, потому что вы вызывали функцию несколько раз, а $OPTIND не определено локально (см. Ответ Адриана). Хотя я благодарен, что вы приняли мой ответ, вам, вероятно, лучше принять его. - person Ansgar Wiechers; 21.05.2013
comment
Код работает только при вставке в оболочку, но не в скрипте. - person kenorb; 30.12.2015
comment
@kenorb Он отлично работает из сценария. Я не выдумываю. - person Ansgar Wiechers; 30.12.2015
comment
Хорошо, я не был уверен, как называется t, мне пришлось вызывать как t -a bc внутри скрипта или добавить t "$@", изначально я думал, что это будет работать без. - person kenorb; 30.12.2015