Поиск по сайту:

Как использовать getopts для анализа параметров сценария оболочки Linux


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

Представляем встроенную функцию getopts

Передача значений в сценарий Bash – довольно простая задача. Вы вызываете свой скрипт из командной строки или из другого скрипта и указываете свой список значений за именем скрипта. Доступ к этим значениям можно получить внутри вашего скрипта как к переменным, начиная с $1 для первой переменной, $2 для второй и так далее.

Но если вы хотите передать опции скрипту, ситуация быстро усложнится. Когда мы говорим об опциях, мы имеем в виду опции, флаги или переключатели, которые могут обрабатывать такие программы, как ls. Им предшествует тире «-», и они обычно служат индикатором для программы, чтобы включить или выключить какой-либо аспект ее функциональности.

Команда ls имеет более 50 параметров, в основном связанных с форматированием вывода. Опция -X (сортировка по расширению) сортирует вывод в алфавитном порядке по расширению файла. Параметр -U (несортированный) выводит список в порядке каталога.

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

Все становится еще сложнее, если некоторые из ваших параметров принимают аргумент, известный как аргумент параметра. Например, ожидается, что за параметром ls -w (width) будет следовать число, представляющее максимальную ширину отображения вывода. И, конечно же, вы можете передавать в свой скрипт другие параметры, которые являются просто значениями данных, которые вообще не являются опциями.

К счастью, getopts справляется с этой сложностью за вас. И поскольку это встроенная функция, она доступна во всех системах с оболочкой Bash, поэтому устанавливать ее нечего.

Примечание: getopts Не getopt

Существует более старая утилита под названием getopt. Это небольшая утилита программа, а не встроенная функция. Существует много разных версий getopt с разным поведением, тогда как встроенный getops соответствует рекомендациям POSIX.

type getopts
type getopt

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

Компромисс заключается в том, что getopts не обрабатывает длинноформатные имена опций с двойным дефисом. Таким образом, вы можете использовать параметры в формате -w , но не «---широкоформатный». С другой стороны, если у вас есть скрипт, который принимает параметры -a , -b и  -c , getopts позволяет комбинировать их, например, -abc, -bca или -bac и так далее.

В этой статье мы обсуждаем и демонстрируем  getopts , поэтому не забудьте добавить последнюю букву s к имени команды.

Краткий обзор: обработка значений параметров

Этот скрипт не использует пунктирные опции, такие как -a или -b. Он принимает «обычные» параметры в командной строке, и доступ к ним внутри скрипта осуществляется как к значениям.

#!/bin/bash

# get the variables one by one
echo "Variable One: $1" 
echo "Variable Two: $2" 
echo "Variable Three: $3"

# loop through the variables
for var in "$@" do 
  echo "$var" 
done

Доступ к параметрам внутри скрипта осуществляется как к переменным $1, $2 или $3 .

Скопируйте этот текст в редактор и сохраните его как файл с именем «variables.sh». Нам нужно сделать его исполняемым с помощью команды chmod. Вам нужно будет выполнить этот шаг для всех сценариев, которые мы обсуждаем. Просто каждый раз подставляйте имя соответствующего файла скрипта.

chmod +x variables.sh

Если мы запустим наш скрипт без параметров, мы получим этот вывод.

./variables.sh

Мы не передали никаких параметров, поэтому у скрипта нет значений для отчета. На этот раз давайте укажем некоторые параметры.

./variables.sh how to geek

Как и ожидалось, переменные $1, $2 и $3 были установлены в значения параметров, и мы видим их напечатанными.

Этот тип обработки параметров «один к одному» означает, что нам нужно заранее знать, сколько параметров будет. Цикл внизу скрипта не заботится о количестве параметров, он всегда перебирает их все.

Если мы предоставляем четвертый параметр, он не присваивается переменной, но цикл по-прежнему обрабатывает его.

./variables.sh how to geek website

Если мы поместим два слова в кавычки, они будут рассматриваться как один параметр.

./variables.sh how "to geek"

Если нам потребуется, чтобы наш сценарий обрабатывал все комбинации параметров, параметров с аргументами и «обычных» параметров типа данных, нам потребуется отделить параметры от обычных параметров. Этого можно добиться, поместив все параметры — с аргументами или без них — перед обычными параметрами.

Но не будем бежать, пока не научимся ходить. Давайте рассмотрим простейший случай обработки параметров командной строки.

Варианты обработки

Мы используем getopts в цикле while. Каждая итерация цикла работает с одной опцией, которая была передана сценарию. В каждом случае переменной OPTION присваивается значение, указанное в getopts.

С каждой итерацией цикла getopts переходит к следующему параметру. Когда вариантов больше нет, getopts возвращает false и цикл while завершается.

Переменная OPTION сопоставляется с шаблонами в каждом из предложений оператора case. Поскольку мы используем оператор case, не имеет значения, в каком порядке параметры указываются в командной строке. Каждый параметр помещается в оператор case, и запускается соответствующее предложение.

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

Скопируйте этот текст в редактор и сохраните его как скрипт с именем «options.sh» и сделайте его исполняемым.

#!/bin/bash

while getopts 'abc' OPTION; do
  case "$OPTION" in 
    a) 
      echo "Option a used" ;;

    b)
      echo "Option b used"
      ;;

    c)
      echo "Option c used"
      ;;

    ?) 
      echo "Usage: $(basename $0) [-a] [-b] [-c]"
      exit 1
      ;;
  esac
done

Это строка, определяющая цикл while.

while getopts 'abc' OPTION; do

За командой getopts следует строка параметров. Здесь перечислены буквы, которые мы собираемся использовать в качестве опций. В качестве опций можно использовать только буквы из этого списка. Таким образом, в этом случае -d будет недействительным. Это будет перехвачено предложением ?), потому что getopts возвращает вопросительный знак «?» для неопознанной опции. Если это произойдет, правильное использование будет напечатано в окне терминала:

echo "Usage: $(basename $0) [-a] [-b] [-c]"

По соглашению, заключение параметра в скобки «[]» в этом типе сообщения о правильном использовании означает, что параметр является необязательным. Команда basename удаляет все пути к каталогам из имени файла. Имя файла сценария хранится в $0 в сценариях Bash.

Давайте использовать этот скрипт с различными комбинациями командной строки.

./options.sh -a
./options.sh -a -b -c
./options.sh -ab -c
./options.sh -cab

Как мы видим, все наши тестовые комбинации параметров анализируются и обрабатываются правильно. Что, если мы попробуем вариант, которого не существует?

./options.sh -d

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

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

Либо отредактируйте файл «options.sh» и добавьте двоеточие в качестве первого символа строки параметров, либо сохраните этот скрипт как «options2.sh» и сделайте его исполняемым.

#!/bin/bash

while getopts ':abc' OPTION; do
  case "$OPTION" in 
    a) 
      echo "Option a used" 
      ;;

    b)
      echo "Option b used"
      ;;

    c)
      echo "Option c used"
      ;;

    ?) 
      echo "Usage: $(basename $0) [-a] [-b] [-c]"
      exit 1
      ;;
  esac
done

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

./options2.sh.sh -d

Использование getopts с опционными аргументами

Чтобы сообщить getopts, что за параметром будет следовать аргумент, поставьте двоеточие «:» сразу после буквы параметра в строке параметров.

Если после «b» и «c» в строке параметров поставить двоеточие, getopt будет ожидать аргументы для этих параметров. Скопируйте этот скрипт в свой редактор и сохраните его как «arguments.sh» и сделайте его исполняемым.

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

Когда getopt обрабатывает параметр с аргументом, этот аргумент помещается в переменную OPTARG. Если вы хотите использовать это значение в другом месте вашего скрипта, вам нужно скопировать его в другую переменную.

#!/bin/bash

while getopts ':ab:c:' OPTION; do

  case "$OPTION" in
    a)
      echo "Option a used"
      ;;

    b)
      argB="$OPTARG"
      echo "Option b used with: $argB"
      ;;

    c)
      argC="$OPTARG"
      echo "Option c used with: $argC"
      ;;

    ?)
      echo "Usage: $(basename $0) [-a] [-b argument] [-c argument]"
      exit 1
      ;;
  esac

done

Давайте запустим это и посмотрим, как это работает.

./arguments.sh -a -b "how to geek" -c reviewgeek
./arguments.sh -c reviewgeek -a

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

А как же обычные параметры? Ранее мы говорили, что знали, что нам придется помещать их в командную строку после любых опций. Посмотрим, что произойдет, если мы это сделаем.

Варианты и параметры микширования

Мы изменим наш предыдущий скрипт, включив в него еще одну строку. Когда цикл while завершится и все параметры будут обработаны, мы попытаемся получить доступ к обычным параметрам. Мы выведем значение в $1.

Сохраните этот скрипт как «arguments2.sh» и сделайте его исполняемым.

#!/bin/bash

while getopts ':ab:c:' OPTION; do

  case "$OPTION" in
    a)
      echo "Option a used"
      ;;

    b)
      argB="$OPTARG"
      echo "Option b used with: $argB"
      ;;

    c)
      argC="$OPTARG"
      echo "Option c used with: $argC"
      ;;

    ?)
      echo "Usage: $(basename $0) [-a] [-b argument] [-c argument]"
      exit 1
      ;;
  esac

done

echo "Variable one is: $1"

Теперь попробуем несколько комбинаций опций и параметров.

./arguments2.sh dave
./arguments2.sh -a dave
./arguments2.sh -a -c how-to-geek dave

Итак, теперь мы видим проблему. Как только используются какие-либо параметры, переменные $1 и далее заполняются флагами параметров и их аргументами. В последнем примере $4 будет содержать значение параметра «dave», но как вы получите к нему доступ в своем скрипте, если не знаете, сколько опций и аргументов будет использоваться?

Ответ заключается в использовании OPTIND и команды shift.

Команда shift отбрасывает первый параметр — независимо от типа — из списка параметров. Другие параметры «перетасовываются», поэтому параметр 2 становится параметром 1, параметр 3 становится параметром 2 и так далее. Таким образом, $2 становится $1, $3 становится $2 и так далее.

Если вы укажете shift число, это удалит столько же параметров из списка.

OPTIND подсчитывает параметры и аргументы по мере их обнаружения и обработки. Как только все опции и аргументы будут обработаны, OPTIND будет на единицу больше, чем количество опций. Поэтому, если мы используем shift для обрезки параметров (OPTIND-1) из списка параметров, у нас останутся обычные параметры в $1 и далее.

Это именно то, что делает этот скрипт. Сохраните этот скрипт как «arguments3.sh» и сделайте его исполняемым.

#!/bin/bash

while getopts ':ab:c:' OPTION; do
  case "$OPTION" in
    a)
      echo "Option a used"
      ;;

    b)
      argB="$OPTARG"
      echo "Option b used with: $argB"
      ;;

    c)
      argC="$OPTARG"
      echo "Option c used with: $argC"
      ;;

    ?)
      echo "Usage: $(basename $0) [-a] [-b argument] [-c argument]"
      exit 1
      ;;
  esac
done

echo "Before - variable one is: $1"
shift "$(($OPTIND -1))"
echo "After - variable one is: $1"
echo "The rest of the arguments (operands)"

for x in "$@"
do
  echo $x
done

Мы запустим это со смесью опций, аргументов и параметров.

./arguments3.sh -a -c how-to-geek "dave dee" dozy beaky mick tich

Мы видим, что до того, как мы вызвали shift, $1 содержит «-a», но после команды сдвига $1 содержит нашу первую не-опцию, параметр без аргументов. Мы можем перебирать все параметры так же легко, как и в скрипте без анализа параметров.

Всегда хорошо иметь варианты

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