getopts вызывается в функции, не собирая флаги

У меня есть следующий скрипт, который я вызываю из своего .bash_profile:

# Set directories based on current path
__set_dirs() {
    currdir=`pwd`
    if [[ ${currdir} =~ "\/path\/to\/main\/(.*)\/working\/([a-z]+)(/?.*)" ]]
    then
        ws=${BASH_REMATCH[1]}
        subdirs=${BASH_REMATCH[3]}
        stgdir=${ts}/${ws}/STAGING${subdirs}
    else
        echo "${currdir} is not a workspace"
        stgdir=""
    fi
}

# Update local version with staging version
upd() {
    __set_dirs
    if [[ -n ${stgdir} ]]
    then
        __overwrite=0
        while getopts "o" opt
        do
            case $opt in
                o)
                    __overwrite=1
                    ;;
                \?)
                    echo "Invalid option: -$OPTARG" >&2
                    ;;
            esac
        done

        echo "Updating ${currdir} with latest from ${stgdir}..."
        if [ ${__overwrite} -eq 0 ]
        then
            update -r ${stgdir} ${currdir}
        else
            echo "Overwriting all local changes!"
            update -r -w ${stgdir} ${currdir}
        fi
    fi
    unset __overwrite
}

Когда я выполняю

> upd -o

Флаг полностью игнорируется — я никогда не вижу сообщения «Перезапись всех локальных изменений!». Я что-то где-то пропустил?

ОБНОВЛЕНИЕ: он работает, но только при первом запуске скрипта. Со второго раза флаг игнорируется.


person abeger    schedule 09.09.2013    source источник


Ответы (2)


Ладно, разобрался:

Просмотрев справочную страницу для getopts, я нашел этот лакомый кусочек ( выделение мое):

При каждом вызове getopts помещает... индекс следующего обрабатываемого аргумента в переменную OPTIND. OPTIND инициализируется значением 1 каждый раз, когда вызывается оболочка или сценарий оболочки.... Оболочка не сбрасывает OPTIND автоматически; его необходимо сбросить вручную между несколькими вызовами getopts в рамках одного и того же вызова оболочки, если должен использоваться новый набор параметров.

Поскольку я запускаю скрипт только один раз из .bashrc, OPTIND инициализируется только один раз. В первый раз, когда я запускаю функцию, все работает отлично. Во второй раз OPTIND устанавливается на 2, а getopts ничего там не находит, поэтому он движется дальше.

Вооружившись этими знаниями, я изменил upd(), чтобы сбросить OPTIND до 1:

upd() {
    __set_dirs
    if [[ -n ${stgdir} ]]
    then
         __overwrite=0
         OPTIND=1
         while getopts "o" opt
...

Это исправило это. OPTIND: важнее, чем ты думаешь.

person abeger    schedule 09.09.2013

getopts действует на параметры, переданные в вашу функцию, поэтому в вашем случае вам нужно вызвать функцию upd() с "$@", чтобы передать все параметры командной строки в вашу функцию.

eg:

test() {
  while getopts "o" opt; do
    case $opt in
    o)
      echo o
    ;;
    \?)
      echo "Invalid option: -$OPTARG" >&2
    ;;
    esac
  done
}
test # this wont work as $1, $2, ... are not set
test $@ # this will work as you pass along the command line parameters

Изменить

Я пропустил часть .bashrc, если я включу приведенный выше пример в свою работающую оболочку, тогда test -o будет работать, как и ожидалось.

person jbr    schedule 09.09.2013
comment
Не забудьте заключить $@ в двойные кавычки для обработки аргументов с пробелами: test "$@" (хотя в данном конкретном случае это не имеет значения) - person ThisSuitIsBlackNot; 09.09.2013
comment
Видишь ли, это работает и для меня. Вот почему я разместил весь код - мне интересно, не сломаю ли я его как-то с какой-то другой частью функции. - person abeger; 10.09.2013