Как мне разобрать аргументы командной строки в Bash? - PullRequest
1611 голосов
/ 10 октября 2008

Скажем, у меня есть скрипт, который вызывается с этой строкой:

./myscript -vfd ./foo/bar/someFile -o /fizz/someOtherFile

или этот:

./myscript -v -f -d -o /fizz/someOtherFile ./foo/bar/someFile 

Какой приемлемый способ синтаксического анализа этого, так что в каждом случае (или некоторой комбинации двух) $v, $f и $d все будут установлены в true и $outFile будут равны до /fizz/someOtherFile?

Ответы [ 31 ]

1 голос
/ 18 октября 2015

Другое решение без getopt [s], POSIX, старый стиль Unix

Похоже на решение Бруно Броноски опубликовал это одно без использования getopt(s).

Основная отличительная черта моего решения заключается в том, что оно позволяет объединять опции, как tar -xzf foo.tar.gz равно tar -x -z -f foo.tar.gz. И так же, как в tar, ps и т. Д., Начальный дефис необязателен для блока коротких опций (но это можно легко изменить). Также поддерживаются длинные опции (но когда блок начинается с одного, тогда требуются два начальных дефиса).

Код с примерами вариантов

#!/bin/sh

echo
echo "POSIX-compliant getopt(s)-free old-style-supporting option parser from phk@[se.unix]"
echo

print_usage() {
  echo "Usage:

  $0 {a|b|c} [ARG...]

Options:

  --aaa-0-args
  -a
    Option without arguments.

  --bbb-1-args ARG
  -b ARG
    Option with one argument.

  --ccc-2-args ARG1 ARG2
  -c ARG1 ARG2
    Option with two arguments.

" >&2
}

if [ $# -le 0 ]; then
  print_usage
  exit 1
fi

opt=
while :; do

  if [ $# -le 0 ]; then

    # no parameters remaining -> end option parsing
    break

  elif [ ! "$opt" ]; then

    # we are at the beginning of a fresh block
    # remove optional leading hyphen and strip trailing whitespaces
    opt=$(echo "$1" | sed 's/^-\?\([a-zA-Z0-9\?-]*\)/\1/')

  fi

  # get the first character -> check whether long option
  first_chr=$(echo "$opt" | awk '{print substr($1, 1, 1)}')
  [ "$first_chr" = - ] && long_option=T || long_option=F

  # note to write the options here with a leading hyphen less
  # also do not forget to end short options with a star
  case $opt in

    -)

      # end of options
      shift
      break
      ;;

    a*|-aaa-0-args)

      echo "Option AAA activated!"
      ;;

    b*|-bbb-1-args)

      if [ "$2" ]; then
        echo "Option BBB with argument '$2' activated!"
        shift
      else
        echo "BBB parameters incomplete!" >&2
        print_usage
        exit 1
      fi
      ;;

    c*|-ccc-2-args)

      if [ "$2" ] && [ "$3" ]; then
        echo "Option CCC with arguments '$2' and '$3' activated!"
        shift 2
      else
        echo "CCC parameters incomplete!" >&2
        print_usage
        exit 1
      fi
      ;;

    h*|\?*|-help)

      print_usage
      exit 0
      ;;

    *)

      if [ "$long_option" = T ]; then
        opt=$(echo "$opt" | awk '{print substr($1, 2)}')
      else
        opt=$first_chr
      fi
      printf 'Error: Unknown option: "%s"\n' "$opt" >&2
      print_usage
      exit 1
      ;;

  esac

  if [ "$long_option" = T ]; then

    # if we had a long option then we are going to get a new block next
    shift
    opt=

  else

    # if we had a short option then just move to the next character
    opt=$(echo "$opt" | awk '{print substr($1, 2)}')

    # if block is now empty then shift to the next one
    [ "$opt" ] || shift

  fi

done

echo "Doing something..."

exit 0

Пример использования приведен ниже в примерах.

Позиция опций с аргументами

Для чего бы то ни было, опции с аргументами не являются последними (нужны только длинные опции). Так, например в tar (по крайней мере, в некоторых реализациях) параметры f должны быть последними, потому что имя файла следует (tar xzf bar.tar.gz работает, но tar xfz bar.tar.gz нет) здесь это не так (см. более поздние примеры).

Несколько параметров с аргументами

В качестве еще одного бонуса параметры опций расходуются в порядке опций параметрами с необходимыми опциями. Просто посмотрите на вывод моего скрипта здесь с командной строкой abc X Y Z (или -abc X Y Z):

Option AAA activated!
Option BBB with argument 'X' activated!
Option CCC with arguments 'Y' and 'Z' activated!

Конкатенация длинных опций

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

  • -cba Z Y X
  • cba Z Y X
  • -cb-aaa-0-args Z Y X
  • -c-bbb-1-args Z Y X -a
  • --ccc-2-args Z Y -ba X
  • c Z Y b X a
  • -c Z Y -b X -a
  • --ccc-2-args Z Y --bbb-1-args X --aaa-0-args

Все это приводит к:

Option CCC with arguments 'Z' and 'Y' activated!
Option BBB with argument 'X' activated!
Option AAA activated!
Doing something...

Не в этом решении

Необязательные аргументы

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

Все становится еще сложнее с несколькими возможными параметрами. Я бы не советовал делать варианты, пытаясь быть умными, определяя, может ли аргумент быть для него или нет (например, если опция просто принимает число в качестве необязательного аргумента), потому что это может сломаться в будущем.

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

Параметры аргументов, представленные знаком равенства

Так же, как и с необязательными аргументами, я не фанат этого (кстати, есть ли тема для обсуждения плюсов / минусов различных стилей параметров?), Но если вы хотите это, вы, вероятно, можете реализовать это сами, как это было сделано в http://mywiki.wooledge.org/BashFAQ/035#Manual_loop с оператором --long-with-arg=?* и последующим удалением знака равенства (кстати, это сайт, который говорит, что создание конкатенации параметров возможно с некоторыми усилиями, но «оставил [это] как упражнение для читателя»), что сделало я беру их на слово, но я начал с нуля).

Другие заметки

POSIX-совместимый, работает даже на старых установках Busybox, с которыми мне приходилось иметь дело (например, cut, head и getopts отсутствуют).

...