Как использовать взаимоисключающие флаги в вашей оболочке и добавить необязательный флаг аргумента (застрял с getopts)

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

shell.sh -a SID 
                              <accepted>
shell.sh -b SID
                              <accepted>
shell.sh -ab SID 
               Message- using ab together is the same as running shell.sh     without   any   options supplying  just SID . Help usage < ya da ya > 
shell.sh 
                Please enter SID at the minimum. Usage < ya da ya >
shell.sh SID
               <accepted>

Я пытаюсь разработать эту логику, используя что-то вроде ниже

while getopts ":a:b:" opt; do
  case $opt in
  a ) SID="$OPTARG";;
      set var=1
  b ) SID="$OPTARG";;
      set var=2

 \?) echo "Invalid option: -"$OPTARG"" >&2
        exit 1;;
  ) echo "Option -"$OPTARG" requires an argument." >&2
        exit 1;;
  esac
done

If  (( val == 1 ))  then ; # option a is invoked SID supplied 
<stuff>
elif  (( val == 2 )) then ; # option b is invoked SID supplied 

<stuff>

else # SID supplied but neither a or b is invoked 
<stuff>
fi

Как применять взаимоисключающие флаги. Я уверен, что есть и другие акробатические способы сделать это. Я думаю, что мне не хватает чего-то здравого смысла, и я пытаюсь понять это. Спасибо

$ more opt.ksh
die () {
    echo "ERROR: $*. Aborting." >&2
    return  1
}

var=
while getopts ":a:b:" opt; do
  case $opt in
      a ) SID="$OPTARG"
          [ "$var" = 2 ] && die "Cannot specify option a after specifying option b"
          [ "$OPTARG" = b ] && die "Do not specify b as a value for option a"
          var=1
          ;;
      b ) SID="$OPTARG"
          [ "$var" = 1 ] && die "Cannot specify option b after specifying option a"
          [ "$OPTARG" = a ] && die "Do not specify a as a value for option b"
          var=2
          ;;
      :)  die "Must supply an argument to $OPTARG"
          ;;
      \?) die "Invalid option: -$OPTARG. Abort"
          ;;
  esac
done
shift $(($OPTIND - 1))
[ "$SID" ] || SID=$1
[ "$SID" ] || die "You must, at the minimum, supply SID"

я использую кш

$ ps -p $$
  PID TTY          TIME CMD
 1261 pts/45   00:00:00 ksh

1 раз запускаю.

$ . opt.ksh -a 123 -b 123  # c0
ERROR: Cannot specify option b after specifying option a. Aborting.
-bash: /home/d1ecom1/gin1: No such file or directory


$ . opt.ksh -ab 123  # c1 should reject "cant use a and b togather. try with a or b"
-nologin: .[25]: shift: 4: bad number
$ . opt.ksh -a -b  # c2  same as c1's message
$ . opt.ksh -a -b 123 # c3 same as c1

$ . opt.ksh -a -b 123  # c5 
$ . opt.ksh -ab 123    # c6
$ . opt.ksh -a 123 -b 123  # c7

Все вышеперечисленные случаи C0:C7 следует отклонить. Обратите внимание, что C0 и C7 одинаковы. Тем не менее, несмотря на это, C0 дает ожидаемую ошибку, а C7 не дает никакой ошибки? странный

должны работать только те

. opt.ksh -a 123
. opt.ksh -b 123
. opt.ksh  123

@hvd: TYSM за ваш ответ. Я хотел бы добавить дополнительный флаг -p, который даст опцию «переопределения пути». поэтому, возможно, нам нужно расширить getopt, чтобы принимать такие параметры

    die () {
    echo "ERROR: $*. Aborting." >&2
    exit 1
}

var=
opta=false
optb=false
while getopts ":ab:p" opt; do
  case $opt in
      a ) $optb && die "Cannot specify option a after specifying option b"
          opta=true
          ;;
      b ) $opta && die "Cannot specify option b after specifying option a"
          optb=true
          ;;
      p )  [ -d "$mypath" ] && die "path is invalid"

          ;;

      \?) die "Invalid option: -$OPTARG. Abort"
          ;;
  esac
done
shift $(($OPTIND - 1))
test $# -eq 0 && die "You must supply SID"
test $# -eq 1 || die "Too many command-line arguments"
# SID=$1

Выше был старый подход. Теперь у меня есть 2 входных параметра. Один — это SID, а другой — путь. Это так же просто, как указано выше, или мне нужно добавить больше проверок, чтобы предотвратить другие нежелательные комбинации. Вопрос, который я пытаюсь задать, заключается в том, что еще нужно сделать, чтобы разрешить этот параметр -p, который является необязательным параметром переопределения. - p может сосуществовать с любым указанным выше параметром, но тогда, я думаю, одно требование состоит в том, что он должен следовать сразу за флагом -p, поэтому это не должно разрешаться, потому что это не ясно.

shell.sh -b -p 123 /home/yadaya

Спасибо еще раз


person user1874594    schedule 11.02.2014    source источник
comment
Почему вы рассматриваете SID как аргумент опции? Если вы просто создадите параметры -a и -b, которые не принимают никаких аргументов (отбрасывая :), и всегда будете проверять, был ли предоставлен не-параметр в качестве SID, это уже будет работать должным образом.   -  person    schedule 11.02.2014
comment
Обновил пост с этими результатами. Все еще застряли   -  person user1874594    schedule 11.02.2014
comment
Вам нужно, чтобы первая и третья строки этой последней части (shift $(($OPTIND - 1)) / [ "$SID" ] || die "You must, at the minimum, supply SID"), которые вы теперь превратили в комментарии, чтобы отклонить -a сам по себе. Вскоре я превращу свой подход в ответ.   -  person    schedule 11.02.2014
comment
Я действительно хочу добавить дополнительный флаг -p /path. Я обновил свой Q. Всякий раз, когда у вас есть шанс. Thx   -  person user1874594    schedule 15.02.2014
comment
Это то, что вам, вероятно, следует задать как новый вопрос, а не как редактирование вашего старого вопроса. Но, в отличие от ваших -a/-b, мне кажется, что это будет хорошо работать в качестве аргумента опции (:abp:, а не :ab:p). В вашем случае p) просто установите mypath=$OPTARG (и подтвердите при необходимости). Остальные проверки останутся прежними, потому что у вас по-прежнему будет только один необязательный аргумент. Однако, хотя -p /path -a 123 кажется вполне допустимым и будет разрешено, то же самое можно сказать и о -a -p /path 123, однако -a 123 -p /path будет отклонено. Вы можете решить, имеет ли это для вас смысл.   -  person    schedule 15.02.2014
comment
Привет, спасибо. -a 123 -p /path (тот последний) - это тоже не кажется правильным, потому что, если значение для -p следует сразу за ним. Shell может понять это. То, что смутит оболочку, это что-то вроде -a -p 100 /path, потому что теперь я поменял значения. Такие комбинации, которые я хочу отсеять.. или, может быть, я слишком внимательно смотрю на это и могу предположить, здравый смысл не проверяет все неправильные случаи   -  person user1874594    schedule 15.02.2014
comment
С моим предложенным подходом -a 123 -p /path не будет допустимым, потому что 123 является аргументом без опций, а аргументы без опций не могут сопровождаться опциями. -a -p 100 /path будет обнаружен вашей проверкой того, что аргумент для -p должен быть допустимым путем, если пользователь не выполняет mkdir 100. И если пользователь создаст этот каталог, то 100 будет допустимым аргументом для -p.   -  person    schedule 15.02.2014
comment
другой вопрос, который у меня был, касался -p (я могу задать еще один вопрос - просто дайте мне знать). Мне нужно проверить 2 вещи: p определен, а также p является допустимым путем, что-то вроде ( [ -z $OPTARG ] && [ -d "$OPTARG" ] ) || die "message" будет работать. Я хочу, чтобы оба эти условия были верны, иначе я умру. Может быть, вы поможете написать это лучше. еще раз спасибо   -  person user1874594    schedule 15.02.2014
comment
Я смущен. Можете ли вы привести пример, когда [ -d "$OPTARG" ] возвращает код успеха, который вы хотите отклонить?   -  person    schedule 15.02.2014
comment
Я говорю это, во-первых, я хочу убедиться, что у вас есть значение (переключатель -p) с помощью опции -z, а затем проверить, является ли это значение допустимым каталогом. У меня была мысль, что -d это проверит. Может быть, я ошибаюсь в синтаксисе. Как бы еще сказать. (значение присутствует && его действующий каталог) или умрет.   -  person user1874594    schedule 15.02.2014
comment
Пустая строка не является допустимым каталогом, поэтому [ -d "$OPTARG" ] уже должен обрабатывать этот случай, и для меня это так.   -  person    schedule 15.02.2014


Ответы (3)


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

die () {
    echo "ERROR: $*. Aborting." >&2
    exit 1
}

var=
opta=false
optb=false
while getopts ":ab" opt; do
  case $opt in
      a ) $optb && die "Cannot specify option a after specifying option b"
          opta=true
          ;;
      b ) $opta && die "Cannot specify option b after specifying option a"
          optb=true
          ;;
      \?) die "Invalid option: -$OPTARG. Abort"
          ;;
  esac
done
shift $(($OPTIND - 1))
test $# -eq 0 && die "You must supply SID"
test $# -eq 1 || die "Too many command-line arguments"
SID=$1

Изменения, которые я сделал:

  • return 1 становится exit 1 (не имеет отношения к вашему вопросу, но die звучит так, как будто это не должно продолжаться). return 1 просто заставит функцию die вернуться безуспешно, но вызовы die не проверяют ее результат, она будет продолжаться независимо.
  • Строка getopts — это :ab вместо :a:b:. Независимо от того, как вы называете свой скрипт, вам всегда нужно передавать ровно один SID, независимо от опций, поэтому мне не имеет смысла включать его как часть опций.
  • Параметры хранятся в логических переменных opta и optb для упрощения проверки.
  • После передачи всех параметров подсчитываются оставшиеся аргументы командной строки. Если это не ровно один, вызов сценария недействителен.

Все ваши действительные вызовы в вопросе принимаются, все недействительные отклоняются. Тем не менее, есть одно интересное замечание по этому поводу: ожидается, что ваш тестовый пример c7 (-a 123 -b 123) завершится неудачно, и он действительно провалится, но не потому, что -a и -b объединены. Вместо этого, в моем подходе, поскольку -b появляется после аргумента, не являющегося параметром, он сам является аргументом, не являющимся параметром, и причиной его отклонения становится «Слишком много аргументов командной строки».

person Community    schedule 11.02.2014
comment
Спасибо за разъяснения. Выход 1 выбивает меня из оболочки :( !!. Если я использую return, он ведет себя шатко - person user1874594; 12.02.2014
comment
@user1874594 user1874594 Верно, это потому, что вы вызываете его с помощью . вместо отдельной оболочки (что чаще встречается в подобных сценариях). return здесь не имеет смысла, так как вы игнорируете статус выхода функции die. Это сработает, если вы назовете его как ksh opt.ksh [args], и это немного сложнее и выходит за рамки того, что, как я думаю, ваш вопрос действительно должен заставить его работать должным образом и для формы .. - person ; 12.02.2014
comment
ТЫ. Так что я бы просто добавил +x и выполнил его напрямую. Позвольте мне попробовать обновить TY снова - person user1874594; 12.02.2014
comment
ТЫ. Так что я бы просто добавил +x и выполнил его напрямую. Позвольте мне попробовать обновить TY еще раз. Работает как шарм. V полезное обучение. ТЮ - person user1874594; 12.02.2014

В случае, когда вы должны указать ровно один из параметров -s, -i или -h и если у вас есть еще два параметра -v и -n, которые являются необязательными, вы можете сделать что-то вроде следующего:

modes=0
while getopts :vs:i:h:n opt; do
  case "$opt" in
    v)
      verbose='non-empty-string-true-flag'
      ;;
    s)
      ((modes++))
      : do something with OPTARG, the s form
      ;;
    i)
      ((modes++))
      : do something with OPTARG, the i form
      ;;
    h)
      ((modes++))
      : do something with OPTARG, the h form
      ;;
    n)
      dryrun='non-empty-string-true-flag'
      ;;
    :)
      printf 'Option -%s requires an argument\n' "$OPTARG" >&2
      usage
      exit 1
      ;;
    *)
      printf 'Invalid option: -%s\n' "$OPTARG" >&2
      usage
      exit 1
      ;;
  esac
done
shift "$((OPTIND-1))"
if [ "$modes" -eq 0 ]; then
  printf "Missing required option\n" >&2
  usage
  exit 1
elif [ "$modes" -gt 1 ]; then
  printf "Error: -h, -i and -s are mutually exclusive and may only be used once\n" >&2
  usage
  exit 1
fi

Это выдержка из моего сценария, где обработка аргументов s, i или h опущена для простоты иллюстрации.

Синопсис сценария выглядит так:

$0 [ -v ] [ -n ] { -i IPADDRESS | -h HOSTNAME | -s HOSTSHA }
person Wildcard    schedule 15.11.2016

Это делает варианты a и b взаимоисключающими:

die () {
    echo "ERROR: $*. Aborting." >&2
    exit 1
}

var=
while getopts ":a:b:" opt; do
  case $opt in
      a ) SID="$OPTARG"
          [ "$var" = 2 ] && die "Cannot specify option a after specifying option b"
          [ "$OPTARG" = b ] && die "Do not specify b as a value for option a"
          var=1
          ;;
      b ) SID="$OPTARG"
          [ "$var" = 1 ] && die "Cannot specify option b after specifying option a"
          [ "$OPTARG" = a ] && die "Do not specify a as a value for option b"
          var=2
          ;;
      :)  die "Must supply an argument to $OPTARG"
          ;;
      \?) die "Invalid option: -$OPTARG. Abort"
          ;;
  esac
done
shift $(($OPTIND - 1))
[ "$SID" ] || SID=$1
[ "$SID" ] || die "You must, at the minimum, supply SID"

Обратите внимание, что optargs интерпретирует shell.sh -ab SID как параметр a с аргументом b, за которым следует SID, как аргумент сценария, а не как часть параметра. Чтобы обнаружить эту проблему, были добавлены строки выше, включающие [ "$OPTARG" = b ] && die... и [ "$OPTARG" = a ] && die....

Вы хотели принять shell.sh SID без указания опции. Это обрабатывается первыми двумя строками после оператора case.

person John1024    schedule 11.02.2014
comment
Привет TY за этот инновационный подход. Это все еще не работает. Я изменил выход в штампе на return1 , иначе он выйдет из моего входа в систему. Когда я использую return 1, результаты совершенно непредсказуемы. я обновил их в вопросе выше - person user1874594; 11.02.2014