Разбор флагов и аргументов с помощью getopts
Реализуйте профессиональные интерфейсы командной строки с помощью getopts для коротких параметров, обязательных аргументов и сообщений об использовании.
«Разбор флагов и аргументов с помощью getopts» — бесплатный урок Linux Command Line & Bash Scripting Mastery на CoddyKit. Это урок 3 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Linux Command Line & Bash Scripting Mastery, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Linux Command Line & Bash Scripting Mastery содержит 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) и разблокировать остальной курс Linux Command Line & Bash Scripting Mastery, подпишись на CoddyKit PRO. Курс Linux Command Line & Bash Scripting Mastery содержит 4 уроков всего.
Чему я научусь в уроке «Разбор флагов и аргументов с помощью getopts»?
Реализуйте профессиональные интерфейсы командной строки с помощью getopts для коротких параметров, обязательных аргументов и сообщений об использовании. Ты практикуешь Linux Command Line & Bash Scripting Mastery с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Linux Command Line & Bash Scripting Mastery?
Предыдущий опыт не требуется. Linux Command Line & Bash Scripting Mastery на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 4.
Сколько времени занимает урок «Разбор флагов и аргументов с помощью getopts»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Linux Command Line & Bash Scripting Mastery?
Да. Каждый урок Linux Command Line & Bash Scripting Mastery включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Проектирование функций с локальной областью и кодами возврата
- Создание и подключение повторно используемых библиотек Bash
- Разбор флагов и аргументов с помощью getopts
- Передача массивов и ассоциативных отображений между функциями