← Весь справочник

getopts

Разбор коротких опций скрипта (-v, -o файл) — строка опций, OPTARG, OPTIND, тихий режим и сравнение с getopt

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 getoptseval 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