0Pricing
DevOps Bootcamp · Урок

Разбор флагов и аргументов с помощью getopts

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

«Разбор флагов и аргументов с помощью getopts» — бесплатный урок DevOps Bootcamp на CoddyKit. Это урок 3 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения DevOps Bootcamp, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс DevOps Bootcamp содержит 4 уроков всего.

Зачем нужен getopts

Любому практическому скрипту рано или поздно потребуется принимать параметры, например -v, -o output.txt или -n 5. Быстрое самостоятельное разбор таких параметров с помощью $1, $2… становится ненадежным.

getopts — встроенный элемент стандарта POSIX, который надежно обрабатывает короткие параметры (-a, -b), в том числе параметры, принимающие аргумент. Он встроен в каждую оболочку POSIX, поэтому дополнительные зависимости не требуются.

  • Обрабатывает объединенные флаги: -vn 5 = -v -n 5
  • Корректно сообщает о неизвестных параметрах
  • Автоматически устанавливает стандартные переменные OPTIND и OPTARG

В этом уроке вы с нуля создадите полноценный профессиональный интерфейс командной строки с помощью getopts.

Синтаксис getopts

Основной синтаксис представляет собой цикл while, который на каждой итерации вызывает getopts:

while getopts "optstring" varname; do
  case "$varname" in
    ...
  esac
done
  • optstring — строка со списком букв всех принимаемых параметров. Двоеточие после буквы означает, что этому параметру требуется аргумент.
  • varname — на каждой итерации получает текущую букву параметра.
  • OPTARG — автоматически получает значение аргумента, если после буквы стоит двоеточие.
  • OPTIND — индекс следующего аргумента для обработки; после цикла используйте shift $((OPTIND - 1)), чтобы сделать оставшиеся позиционные параметры доступными.
#!/usr/bin/env bash
# Minimal skeleton — shows the loop structure
while getopts 'vn:' opt; do
  case "$opt" in
    v) echo "Verbose mode on" ;;
    n) echo "Count = $OPTARG" ;;
    ?) echo "Unknown option: -$OPTARG" >&2; exit 1 ;;
  esac
done

Определение optstring

optstring — это компактное объявление вашего соглашения об интерфейсе командной строки. Каждый символ обозначает один принимаемый флаг.

  • 'abc' — принимает -a, -b, -c (без аргументов)
  • 'a:bc' — для -a требуется аргумент; для -b и -c аргумент не требуется
  • ':abc' — начальное двоеточие включает режим без сообщений об ошибках (скрипт сам обрабатывает недопустимые параметры, вместо того чтобы оболочка выводила сообщение)

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

#!/usr/bin/env bash
# optstring ':o:vq'
# -o  requires an argument (output file)
# -v  verbose flag (no argument)
# -q  quiet flag  (no argument)
# Leading ':' = silent error mode

while getopts ':o:vq' opt; do
  case "$opt" in
    o) OUTPUT="$OPTARG" ;;
    v) VERBOSE=1 ;;
    q) QUIET=1 ;;
    :) echo "Error: -$OPTARG requires an argument" >&2; exit 1 ;;
    \?) echo "Error: unknown option -$OPTARG" >&2; exit 1 ;;
  esac
done
echo "OUTPUT=$OUTPUT  VERBOSE=$VERBOSE  QUIET=$QUIET"

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

Если в optstring после флага стоит двоеточие, getopts сохраняет его значение в OPTARG. Пользователь может указать аргумент как с пробелом, так и без него:

  • -o report.txt
  • -oreport.txt

Обе формы разбираются одинаково. Это одно из ключевых преимуществ по сравнению с самостоятельным разбором с помощью $1/shift.

В режиме без сообщений (если optstring начинается с ':') отсутствие аргумента заставляет getopts установить в varname значение :, а в OPTARG — букву параметра. Это идеально подходит для точного сообщения об ошибке.

#!/usr/bin/env bash
# Demonstrate OPTARG with a file-processing script

while getopts ':i:o:' opt; do
  case "$opt" in
    i) INPUT="$OPTARG" ;;
    o) OUTPUT="$OPTARG" ;;
    :) echo "Error: option -$OPTARG needs a value" >&2; exit 1 ;;
    \?) echo "Error: unknown flag -$OPTARG" >&2; exit 1 ;;
  esac
done

echo "Input  : ${INPUT:-<not set>}"
echo "Output : ${OUTPUT:-<not set>}"

Переход за пределы параметров с помощью OPTIND

После завершения работы getopts в OPTIND хранится индекс первого аргумента, не являющегося параметром. Используйте shift, чтобы удалить все обработанные параметры, тогда $1, $2… будут ссылаться на оставшиеся позиционные параметры, например имена файлов.

Идиоматическая запись всегда выглядит так:

shift $((OPTIND - 1))

После сдвига $@ содержит только аргументы, которые не были флагами, то есть операнды вашей команды.

#!/usr/bin/env bash
# Shows OPTIND shift and leftover positional args

VERBOSE=0
while getopts ':vn:' opt; do
  case "$opt" in
    v) VERBOSE=1 ;;
    n) COUNT="$OPTARG" ;;
    :) echo "Error: -$OPTARG requires an argument" >&2; exit 1 ;;
    \?) echo "Error: unknown option -$OPTARG" >&2; exit 1 ;;
  esac
done
shift $((OPTIND - 1))

echo "VERBOSE=$VERBOSE  COUNT=${COUNT:-1}"
echo "Remaining args: $*"

Написание функции usage

Профессиональный скрипт всегда предоставляет функцию usage(), которая выводит справочное сообщение и завершает работу. Принято соблюдать следующие правила:

  • Выводить сообщение в stderr (дескриптор 2), чтобы оно не смешивалось с выводом конвейера
  • Завершать работу с кодом 0 для -h / --help и с кодом 1 при недопустимом использовании
  • Вызывать usage 1 в ветках обработки ошибок и usage 0 в обработчике -h
#!/usr/bin/env bash

usage() {
  cat >&2 <<EOF
Usage: $(basename "$0") [-v] [-n COUNT] [-o FILE] [FILE...]

Options:
  -v          Verbose output
  -n COUNT    Repeat COUNT times (default: 1)
  -o FILE     Write output to FILE
  -h          Show this help
EOF
  exit "${1:-0}"
}

while getopts ':vn:o:h' opt; do
  case "$opt" in
    v) VERBOSE=1 ;;
    n) COUNT="$OPTARG" ;;
    o) OUTFILE="$OPTARG" ;;
    h) usage 0 ;;
    :) echo "Error: -$OPTARG requires a value" >&2; usage 1 ;;
    \?) echo "Error: unknown option -$OPTARG" >&2; usage 1 ;;
  esac
done
shift $((OPTIND - 1))
echo "Parsed OK — verbose=${VERBOSE:-0} count=${COUNT:-1} out=${OUTFILE:--}"

Значения по умолчанию и проверка

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

  • Используйте ${VAR:-default} для задания значений по умолчанию непосредственно в выражении
  • Проверяйте числовые аргументы с помощью регулярного выражения или арифметической проверки
  • Проверяйте, что обязательные параметры действительно были переданы
#!/usr/bin/env bash

usage() { echo "Usage: $(basename "$0") -n COUNT [-v]" >&2; exit 1; }

VERBOSE=0
COUNT=''

while getopts ':n:v' opt; do
  case "$opt" in
    n) COUNT="$OPTARG" ;;
    v) VERBOSE=1 ;;
    :) echo "Error: -$OPTARG needs a value" >&2; usage ;;
    \?) echo "Error: -$OPTARG unknown" >&2; usage ;;
  esac
done
shift $((OPTIND - 1))

# Validation phase
[[ -z "$COUNT" ]] && { echo "Error: -n COUNT is required" >&2; usage; }
[[ "$COUNT" =~ ^[0-9]+$ ]] || { echo "Error: COUNT must be a positive integer" >&2; usage; }

for (( i=1; i<=COUNT; i++ )); do
  [[ $VERBOSE -eq 1 ]] && echo "Iteration $i of $COUNT"
  echo "Hello, world!"
done

Объединение флагов в командной строке

getopts автоматически обрабатывает объединенные короткие флаги, записанные без пробелов, что соответствует стандартному соглашению Unix:

  • -v -q эквивалентно -vq
  • -n 5 -v эквивалентно -n5 -v или -vn5

Для поддержки этого не нужно писать дополнительный код: getopts автоматически перебирает каждый символ объединенной строки параметров. Это еще одна важная причина использовать getopts вместо самостоятельного разбора.

#!/usr/bin/env bash
# Test combined flag parsing
# Run as:  bash script.sh -vq -n3

VERBOSE=0; QUIET=0; COUNT=1

while getopts ':vqn:' opt; do
  case "$opt" in
    v) VERBOSE=1 ;;
    q) QUIET=1 ;;
    n) COUNT="$OPTARG" ;;
    :) echo "Error: -$OPTARG needs value" >&2; exit 1 ;;
    \?) echo "Error: unknown -$OPTARG" >&2; exit 1 ;;
  esac
done
shift $((OPTIND - 1))

echo "verbose=$VERBOSE quiet=$QUIET count=$COUNT"

Обработка разделителя из двух дефисов

Команды Unix принимают -- (два дефиса) как явный сигнал о прекращении обработки параметров. Все после -- считается позиционным аргументом, даже если выглядит как флаг.

getopts автоматически прекращает работу, встретив --. После shift $((OPTIND - 1)) разделитель из двух дефисов исчезает, а $@ содержит только операнды.

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

myscript.sh -v -- -strangefile.txt
#!/usr/bin/env bash
# Demonstrate -- separator
# Run as:  bash script.sh -v -- file1.txt -oddname.txt

VERBOSE=0
while getopts ':v' opt; do
  case "$opt" in
    v) VERBOSE=1 ;;
    \?) echo "Unknown option -$OPTARG" >&2; exit 1 ;;
  esac
done
shift $((OPTIND - 1))   # removes -v and the '--' separator

echo "Verbose: $VERBOSE"
echo "Files to process:"
for f in "$@"; do
  echo "  -> $f"
done

Обертка getopts в библиотечную функцию

В модульных скриптах getopts можно инкапсулировать в функции parse_args(), которая задает глобальные переменные (или переменные, переданные по ссылке). Это сохраняет чистоту main() и позволяет подключать разбор параметров из других скриптов.

Основные правила этого шаблона:

  • Объявляйте переменные параметров до вызова функции
  • Используйте global или передавайте значения через nameref (declare -n)
  • При некорректных входных данных возвращайте ненулевой код завершения, чтобы main мог отреагировать
#!/usr/bin/env bash

# Global option variables
VERBOSE=0; OUTPUT=''; COUNT=1

parse_args() {
  local opt
  while getopts ':vn:o:h' opt; do
    case "$opt" in
      v) VERBOSE=1 ;;
      n) COUNT="$OPTARG" ;;
      o) OUTPUT="$OPTARG" ;;
      h) echo "Usage: $(basename "$0") [-v] [-n N] [-o FILE]"; exit 0 ;;
      :) echo "Error: -$OPTARG needs a value" >&2; return 1 ;;
      \?) echo "Error: unknown option -$OPTARG" >&2; return 1 ;;
    esac
  done
  shift $((OPTIND - 1))
  ARGS=("$@")   # leftover positional args stored in array
}

main() {
  parse_args "$@" || exit 1
  echo "verbose=$VERBOSE count=$COUNT output=${OUTPUT:--} args=${ARGS[*]}"
}

main "$@"

Полный практический пример: архиватор журналов

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

Изучите структуру: это шаблон, которому следует придерживаться в каждом написанном вами скрипте, которому требуется интерфейс командной строки.

#!/usr/bin/env bash
# archive_logs.sh — compress and move logs older than N days

set -euo pipefail

DESTDIR='/tmp/log_archive'
DAYS=30
VERBOSE=0

usage() {
  cat >&2 <<EOF
Usage: $(basename "$0") [-v] [-d DAYS] [-o DIR] SOURCE_DIR

  -d DAYS   Archive logs older than DAYS (default: 30)
  -o DIR    Destination directory (default: /tmp/log_archive)
  -v        Verbose output
  -h        Show this help
EOF
  exit "${1:-0}"
}

while getopts ':d:o:vh' opt; do
  case "$opt" in
    d) DAYS="$OPTARG" ;;
    o) DESTDIR="$OPTARG" ;;
    v) VERBOSE=1 ;;
    h) usage 0 ;;
    :) echo "Error: -$OPTARG requires a value" >&2; usage 1 ;;
    \?) echo "Error: unknown option -$OPTARG" >&2; usage 1 ;;
  esac
done
shift $((OPTIND - 1))

# Validation
[[ $# -lt 1 ]] && { echo "Error: SOURCE_DIR is required" >&2; usage 1; }
[[ "$DAYS" =~ ^[0-9]+$ ]] || { echo "Error: DAYS must be numeric" >&2; exit 1; }
SOURCE="$1"
[[ -d "$SOURCE" ]] || { echo "Error: '$SOURCE' is not a directory" >&2; exit 1; }

mkdir -p "$DESTDIR"
[[ $VERBOSE -eq 1 ]] && echo "Archiving logs older than $DAYS days from $SOURCE to $DESTDIR"

find "$SOURCE" -name '*.log' -mtime "+$DAYS" -print | while read -r f; do
  gzip -c "$f" > "$DESTDIR/$(basename "$f").gz"
  [[ $VERBOSE -eq 1 ]] && echo "  archived: $f"
done

echo "Done."

Быстрая проверка: optstring в getopts

Прочитайте следующий вызов getopts и выберите правильное описание его поведения:

while getopts ':f:vq' opt; do

Итоги урока: освоение getopts

Вы научились создавать профессиональные интерфейсы командной строки в Bash с помощью getopts. Ниже приведены основные моменты:

  • Синтаксис optstring — буквы без двоеточия являются логическими флагами; двоеточие после буквы означает, что ей требуется аргумент; начальное двоеточие включает режим без сообщений об ошибках.
  • OPTARG — автоматически хранит значение аргумента для параметров, которым он требуется.
  • OPTIND — после цикла используйте shift $((OPTIND - 1)), чтобы получить оставшиеся позиционные параметры в $@.
  • Режим без сообщений об ошибках — предпочтителен в рабочих скриптах; самостоятельно обрабатывайте случаи : (отсутствует аргумент) и \? (неизвестный параметр), чтобы полностью контролировать поведение.
  • Функция usage() — всегда создавайте ее; выводите сообщение в stderr, завершайте работу с кодом 0 для -h и с кодом 1 при ошибках.
  • Объединенные флаги — getopts автоматически обрабатывает -vq и -n5 без дополнительного кода.
  • Модульный шаблон — оборачивайте getopts в функцию parse_args(), чтобы скрипты оставались чистыми и повторно применяемыми.

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

Часто задаваемые вопросы

Урок «Разбор флагов и аргументов с помощью getopts» бесплатный?

Да — полный текст урока «Разбор флагов и аргументов с помощью getopts» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс DevOps Bootcamp, подпишись на CoddyKit PRO. Курс DevOps Bootcamp содержит 4 уроков всего.

Чему я научусь в уроке «Разбор флагов и аргументов с помощью getopts»?

Реализуйте профессиональные интерфейсы командной строки с помощью getopts для коротких параметров, обязательных аргументов и сообщений об использовании. Ты практикуешь DevOps Bootcamp с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать DevOps Bootcamp?

Предыдущий опыт не требуется. DevOps Bootcamp на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 4.

Сколько времени занимает урок «Разбор флагов и аргументов с помощью getopts»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке DevOps Bootcamp?

Да. Каждый урок DevOps Bootcamp включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Проектирование функций с локальной областью и кодами возврата
  2. Создание и подключение повторно используемых библиотек Bash
  3. Разбор флагов и аргументов с помощью getopts
  4. Передача массивов и ассоциативных отображений между функциями
← Назад к DevOps Bootcamp