2013-07-25 5 views
1

Я создаю скрипт bash, который включает в себя аргументы анализа. Использование было бы:Разбор флага со списком значений

$ ./my_script.sh -a ARG_1 -b ARG_2 [-c LIST_OF_ARGS...] 

Использование getopts Я могу разобрать -a и -b и получить соответствующие им значения ARG_1 и ARG_2. Если и только если пользователь помещает -c в качестве последнего аргумента, тогда я также могу получить -c и создать список со всеми значениями в LIST_OF_ARGS....

Но я бы не хотел, чтобы пользователь вставлял -c в качестве последнего флага. Например, было бы здорово, если сценарий может быть вызван следующим образом:

$ ./my_script.sh -b ARG_2 -c V1 V2 V3 -a ARG_1 

Вот мой текущий код:

while getopts a:b:c opt 
do 
    case $opt in 
     a) 
      A_FLAG=$OPTARG 
      ;; 
     b) 
      B_FLAG=$OPTARG 
      ;; 
     c) 
      # Handle values as regular expressions 
      args=("[email protected]") 
      C_LIST=() 
      for ((i=$OPTIND-1 ; i <= $#-1 ; i++)) 
      do 
       C_LIST=("${C_LIST[@]}" ${args[$i]}) 
      done 
      ;; 
     ?) 
      usage 
      ;; 
    esac 
done 
+1

'getopts' не поддерживает опции с несколькими аргументами. Вам необходимо проанализировать аргументы вручную, подсчитать аргументы, которые следуют за '-c' в качестве аргументов для этой опции, до тех пор, пока вы не достигнете другого определенного параметра или конца списка. – chepner

+1

В вашем существующем коде вам было бы лучше написать более простое выражение: 'c) C_LIST = (" $ OPTARG "" $ {@: $ OPTIND} ") ;;' Это будет правильно обрабатывать регистр '-cV1 V2 V3 'и это также намного короче. (Также научитесь использовать '+ =' на массивах.) Это не отвечает на ваш вопрос. – rici

ответ

1

В моей системе я гавань /usr/share/doc/util-linux/examples/getopt-parse.bash файла. Он помещает результат getopt в переменную и устанавливает параметры позиционирования для этой переменной. Затем использует switch, аналогичный вашему, но использует shift для удаления аргументов при их обнаружении.

Вы можете сделать что-то подобное, но для вашего -c используйте опцию shift, пока не получите опцию или не закончите аргументы.


Или это может быть достаточно для того, чтобы использовать текущее решение, но не забудьте установить переменную OPTIND после цикла.

1

Необходимо связать ваше обнаружение флага -c с обработкой, связанной с ним. Например, что-то вроде:

while getopts a:b:c opt 
do 
    case $opt in 
     a) 
      A_FLAG=$OPTARG 
      ;; 
     b) 
      B_FLAG=$OPTARG 
      ;; 
     c) 
      C_FLAG=1 
      ;; 
     ?) 
      usage 
      ;; 
    esac 
done 

# discard all of our options. 
shift `expr $OPTIND - 1` 

if [ "$C_FLAG" = 1 ]; then 
      # Handle values as regular expressions 
      args=("[email protected]") 
      C_LIST=() 
      for ((i=0 ; i <= $#-1 ; i++)) 
      do 
       C_LIST=("${C_LIST[@]}" ${args[$i]}) 
      done 
fi 

Этот скрипт не собирает все аргументы без опций до после обработки все параметры командной строки.

+0

Ваше решение не работает, так как оно не обрабатывает ввод '-a ARG_1 -c LIST_OF_ARGS ... -b ARG_2'. После чтения аргументы цикла for получают '-c LIST_OF_ARGS ... -b ARG_2', но в этот момент я ожидаю, что -b уже может быть проанализирован. –

+0

Непонятно, из вашего вопроса, что вы ожидали, что определенная перестановка будет работать. – larsks

1

Возникла проблема: почему у вас есть опция -c?

Если полное использование включает в себя список значений, почему бы просто не иметь опцию -c и разрешить параметры -a и -b только тогда, когда остальные являются регулярными args, как в ./myscript.sh -a ARG_1 -b ARG_2 [argument ...], где любые аргументы являются необязательными (например, -c и его аргументы приведены в вашем примере использования?

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

Конечно, разбор - это трудный путь. Другая возможность заключается в добавлении значений после -c в список, до тех пор, пока вы не сталкиваетесь другой вариант или конец опций:

C_LIST=() 
while getopts a:b:c: opt; do 
    #Skipping code... 
    c) 
     C_LIST+="$OPTARG" 
     shift $(expr $OPTIND - 1) 
     while [ -n "$1" ] && [ $(printf "%s" "$1" | grep -- '^[^-]') ]; do 
      C_LIST+="$1" 
      shift 
     done 
     OPTIND=1 
     ;; 

Поведение getopts имитируется: даже если OPTARG начинается с «-» характер, она по-прежнему сохраняется , но после OPTARG любая строка, начинающаяся с символа '-', может быть просто недопустимой опцией, такой как -n.Я использовал printf вместо echo, потому что в некоторых версиях эха, таких как тот, который имеет встроенный bash, есть опция -e, которая может или не разрешает цикл продолжать, что нежелательно. Выражение grep должно помешать этому, но кто знает, позволяет ли эта версия эха использовать -e'hello ', что вызовет успех grep, потому что видит «привет»? Хотя, возможно, нет необходимости, зачем рисковать?

Лично я бы избегал такого поведения, если вы можете, но я также не понимаю, почему вы просите об этом в первую очередь. Если бы я рекомендовал что-либо, я бы предложил более общий стиль /path/to/script -a ARG_1 -b ARG_2 [argument ...] над любым другим возможным вариантом реализации.

+0

@Joachim Pileborg, конечно, '/ path/to/script -a ARG_1 -b ARG_2 [argument ...]' будет соответствовать моим потребностям. Но в общем случае это не соответствовало бы использованию '/ path/to/script -a ARG_1 -b ARG_2 [-c LIST ...] [-d ANOTHER_LIST ...]. Очевидно, решение @ larsks будет работать –

Смежные вопросы