Массовые аргументы (операнды) на первом месте при передаче аргументов командной строки

Я использую следующие строки (надеюсь, это лучшая практика, если не поправьте меня, пожалуйста) для обработки параметров командной строки:

#!/usr/bin/bash

read -r -d '' HELP <<EOF
OPTIONS:
-c    Enable color output
-d    Enable debug output
-v    Enable verbose output
-n    Only download files (mutually exclusive with -r)
-r    Only remove files (mutually exclusive with -n)
-h    Display this help
EOF

# DECLARE VARIABLES WITH DEFAULT VALUES
color=0
debug=0
verbose=0
download=0
remove=0


OPTIND=1                # Reset in case getopts has been used previously in the shell
invalid_options=();     # Array for invalid options

while getopts ":cdvnrh" opt; do
  echo "Actual opt: $opt"
  case $opt in
    c)
      color=1
      ;;
    d)
      debug=1
      ;;
    v)
      verbose=1
      ;;
    n)
      download=1
      ;;
    r)
      remove=1
      ;;
    h)
      echo "$HELP"
      exit 1
      ;;
   \?)
      invalid_options+=($OPTARG)
      ;;
   *)
      invalid_options+=($OPTARG)
      ;;
  esac
done

# HANDLE INVALID OPTIONS
if [ ${#invalid_options[@]} -ne 0 ]; then
  echo "Invalid option(s):" >&2
  for i in "${invalid_options[@]}"; do
    echo $i >&2
  done
  echo "" >&2
  echo "$HELP" >&2
  exit 1
fi

# SET $1 TO FIRST MASS ARGUMENT, $2 TO SECOND MASS ARGUMENT ETC
shift $((OPTIND - 1))

# HANDLE CORRECT NUMBER OF MASS OPTIONS
if [ $# -ne 2 ]; then
  echo "Correct number of mass arguments are 2"
  echo "" >&2
  echo "$HELP" >&2
  exit 1
fi


# HANDLE MUTUALLY EXCLUSIVE OPTIONS
if [ $download -eq 1 ] && [ $remove -eq 1 ]; then
  echo "Options for download and remove are mutually exclusive" >&2
  echo "$HELP" >&2
  exit 1
fi



echo "color:    $color"
echo "debug:    $debug"
echo "verbose:  $verbose"
echo "download: $download"
echo "remove:   $remove"
echo "\$1:      $1"
echo "\$2:      $2"

Если я вызову сценарий таким образом, что массовые аргументы (те, которые не являются переключателями или аргументами для переключатели) последние аргументы все работает правильно:

$ ./getopts.sh -c -d -v -r a b
Actual opt: c
Actual opt: d
Actual opt: v
Actual opt: r
color:    1
debug:    1
verbose:  1
download: 0
remove:   1
$1:      a
$2:      b

Проблема в том, что когда я хочу вызвать скрипт, чтобы массовые аргументы были первыми (или где-то в середине переключателей, которые не используют аргументы)

$ ./getopts.sh a b -c -d -v -r
Correct number of mass arguments are 2

OPTIONS:
-c    Enable color output
-d    Enable debug output
-v    Enable verbose output
-n    Only download files (mutually exclusive with -r)
-r    Only remove files (mutually exclusive with -d)
-h    Display this help

or

$ ./getopts.sh -c a b -d -v -r
Actual opt: c
Correct number of mass arguments are 2

OPTIONS:
-c    Enable color output
-d    Enable debug output
-v    Enable verbose output
-n    Only download files (mutually exclusive with -r)
-r    Only remove files (mutually exclusive with -d)
-h    Display this help

Я думаю, что это должно быть в порядке в соответствии со стандартами (POSIX), потому что следующий синтаксис, который в основном такой же, работает, как и ожидалось, в моей системе:

$ cp test1/ test2/ -r
$ cp test1/ -r test2/

У меня есть поиск в Интернете, но единственное, что было близко к моей проблеме, это этот относящийся к Ц.


person Wakan Tanka    schedule 18.02.2015    source источник
comment
Ваши наблюдения верны: getopts возвращает статус выхода, когда встречается первый аргумент, не являющийся параметром, поэтому цикл while заканчивается. Я считаю, что getopt может работать более гибко, как вы хотите.   -  person glenn jackman    schedule 18.02.2015
comment
@glennjackman есть рекомендации по использованию getopts вместо getopt. AFAIK getopt считается устаревшим.   -  person Wakan Tanka    schedule 18.02.2015
comment
Я не считаю, что getopt считается устаревшим. getopts проще в использовании (IMO: это то, что я использую), но он заставляет вас помещать параметры перед требуемыми параметрами, и вы не можете использовать --longoptions.   -  person glenn jackman    schedule 18.02.2015
comment
Я просто использую обычный оператор case и shift для того же, но более универсального эффекта.   -  person    schedule 18.02.2015


Ответы (2)


getopts автоматически прерывает цикл while, как только обнаруживает параметр, не являющийся тире (не включая аргумент, переданный параметрам дефиса, которые принимают аргументы). Стандарт POSIX заключается в том, что сначала указываются пунктирные параметры, а затем < em>файлы. Там тоже нет этой -- и + хрени. Это просто и понятно.

Однако Linux не совместим с Unix или POSIX. Утилиты GNU просто в природе "лучше", чем стандартные утилиты Unix. Больше функций, больше возможностей и немного другая обработка вещей.

В Linux параметры командной строки могут идти после файлов во многих утилитах GNU.

Например:

$ cp -R foo bar

Работайте на моем Mac OS X, сертифицированном для Unix, и на Linux.

$ cp foo bar -R

Работает только на линуксе.

Если вы хотите, чтобы getopts работало как многие утилиты Linux, вам нужно немного поработать.

Во-первых, вы должны сами обрабатывать свои аргументы, а не зависеть от $OPTIND их разбора. Вам также необходимо убедиться, что у вас есть аргумент.

Я придумал это как пример делать то, что вы хотите.

#! /bin/bash

while [[ $* ]]
do
    OPTIND=1
    echo $1
    if [[ $1 =~ ^- ]]
    then
        getopts :a:b:cd parameter
        case $parameter in
            a)  echo "a"
                echo "the value is $OPTARG"
                shift
                ;;
            b)  echo "b"
                echo "the value is $OPTARG"
                shift
                ;;
            c)  echo "c"
                ;;
            d)  echo "d"
                ;;
            *) echo "This is an invalid argument: $parameter"
                ;;
        esac
    else
        other_arguments="$other_arguments $1"
    fi
    shift
done
echo "$other_arguments"

Теперь я зацикливаюсь, пока установлено $*. (Может быть, мне следует использовать $@?) Я должен сделать shift в конце цикла. Я также каждый раз сбрасываю $OPTIND на 1, потому что я смещаю аргументы с себя. $OPTARG все еще установлено, но мне нужно сделать еще shift, чтобы убедиться, что все работает.

Я также должен проверить, начинается ли аргумент с тире или не используется регулярное выражение в моем операторе if.

Базовое тестирование показывает, что он работает, но я не могу сказать, что он безошибочен, но он дает вам представление о том, как вы должны обращаться со своей программой.

getopts дает еще много энергии, но для этого требуется немного больше работы.

person David W.    schedule 18.02.2015

Bash предоставляет два метода разбора аргументов.

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

Внешняя команда getopt — это старый и более сложный механизм разбора аргументов. Он позволяет использовать длинные/короткие параметры, а расширение gnu позволяет смешивать параметры и массовые аргументы.

person Zaboj Campula    schedule 18.02.2015