getopts — встроенная команда bash для разбора опций в скрипте или функции: -v, -o файл, склеенных -vo файл. Её вызывают в цикле while: за один вызов она достаёт одну опцию, кладёт её букву в переменную, а аргумент опции — в OPTARG. Длинные опции (--verbose) getopts не умеет — для них есть внешняя утилита getopt или ручной разбор через case. Справка — help getopts.
Синтаксис
while getopts 'строка_опций' имя; do
case $имя in
…
esac
done
shift $((OPTIND - 1)) # убрать разобранные опции, остаются позиционные аргументы
Строка опций
| Запись | Что значит |
|---|
vh | опции -v и -h без аргументов |
o: | опция -o с обязательным аргументом: -o файл или -oфайл |
:vo: | двоеточие в начале — тихий режим: ошибки не печатаются, их обрабатывает твой case |
Переменные
| Переменная | Что содержит |
|---|
имя (из getopts … имя) | букву найденной опции; ? — неизвестная опция; : — нет аргумента (только в тихом режиме) |
OPTARG | аргумент опции; в тихом режиме при ошибке — сама проблемная буква |
OPTIND | номер следующего аргумента для разбора; в начале скрипта равен 1 |
OPTERR=0 | отключить сообщения об ошибках, не включая тихий режим |
Ошибки: обычный и тихий режим
| Ситуация | Обычный режим (vo:) | Тихий режим (:vo:) |
|---|
неизвестная опция -x | печатает illegal option -- x, имя=?, OPTARG не задан | молчит, имя=?, OPTARG=x |
-o без аргумента | печатает option requires an argument -- o, имя=? | молчит, имя=:, OPTARG=o |
getopts возвращает код 1 и цикл заканчивается, когда опции кончились: встретился первый аргумент не с -, отдельное -- или аргументы закончились.
Примеры
#!/bin/bash
# скрипт с опциями: -v, -o файл, -h
usage() { echo "использование: $0 [-v] [-o файл] аргумент…" >&2; }
verbose=0
out=/dev/stdout
while getopts ':vo:h' opt; do
case $opt in
v) verbose=1 ;;
o) out=$OPTARG ;;
h) usage; exit 0 ;;
:) echo "опции -$OPTARG нужен аргумент" >&2; usage; exit 2 ;;
\?) echo "неизвестная опция -$OPTARG" >&2; usage; exit 2 ;;
esac
done
shift $((OPTIND - 1))
(( $# > 0 )) || { usage; exit 2; }
echo "verbose=$verbose out=$out аргументы: $*"
# как этот скрипт разбирает разные вызовы
./script.sh -v -o res.txt a b # verbose=1 out=res.txt аргументы: a b
./script.sh -vores.txt a # то же: опции склеены, аргумент слитно
./script.sh -- -v # после -- всё — аргументы: аргументы: -v
./script.sh a -v # разбор останавливается на a: -v — обычный аргумент
# опция, которую можно повторять: собрать значения в массив
tags=()
while getopts 't:' opt; do
[[ $opt == t ]] && tags+=("$OPTARG")
done
# getopts в функции: сбрось OPTIND, иначе второй вызов ничего не разберёт
f() {
local OPTIND opt
while getopts 'a' opt; do echo "опция $opt"; done
}
f -a; f -a
# длинные опции — через внешний getopt (util-linux)
args=$(getopt -o vo: -l verbose,output: -- "$@") || exit 2
eval set -- "$args"
while true; do
case $1 in
-v|--verbose) verbose=1; shift ;;
-o|--output) out=$2; shift 2 ;;
--) shift; break ;;
esac
done
getopts или getopt
| getopts (встроенная) | getopt (util-linux) |
|---|
длинные опции --name | нет | да: -l name,output: |
опции после аргументов (file -v) | нет, разбор останавливается | да, переставляет опции вперёд |
| переносимость | любая POSIX-оболочка | только Linux с util-linux |
| как использовать | цикл while getopts | eval set -- "$(getopt …)", затем цикл по $1 |
Подводные камни
- Забытый
shift $((OPTIND - 1)). Без него $1 по-прежнему указывает на первую опцию, а не на первый позиционный аргумент.
getopts не понимает длинные опции. --verbose она разберёт как набор букв -, v, e, r, … и выдаст ошибку на каждой неизвестной. Нужны длинные опции — бери getopt.
- Аргументом опции становится следующее слово, даже если оно похоже на опцию.
-o -v даст OPTARG=-v, а -v как отдельная опция не сработает.
- Разбор останавливается на первом позиционном аргументе. В
script.sh file -v опция -v не распознаётся. Ставь опции перед аргументами.
OPTIND не сбрасывается сам. В функции, которую вызывают несколько раз, объяви local OPTIND, иначе второй вызов опций не увидит.
? в case надо экранировать: \?) или '?'). Без этого ?) — шаблон «любой один символ», и под него попадут все опции.
- Код возврата
getopt проверяй до eval: args=$(getopt …) || exit 2. При неизвестной опции getopt печатает ошибку и возвращает 1.
См. также
functions · conditions-loops · variables · exit-codes