Аргументы в bash
Если скрипт имеет более одного параметра - придется проанализировать аргументы ради гарантии правильной обработки всех возможных способов передачи аргументов юзером. Да даже скрипту с одним параметром не помешает обработка параметров -h
/--help
.
Не стоит использовать позиционные параметры $1
, $2
, …) как универсальное имя, ибо не понятно, что делает этот параметр и зачем. Лучше в начале скрипта присвоить их значения переменным с “говорящими” именами.
Простая проверка кол-ва аргументов $#
- кол-во аргументов):
Чтобы сослаться на все аргументы скрипта и заключить каждый из них в кавычки, можно использовать $@
(строка) или ${@}
(список). $*
вернет одну большую строку в кавычках со всеми аргументами. Например, при таком вызове скрипта:
$*
вернет одну строку "file1.dat alt data local data summary.txt"
, тогда как $@
- четыре отдельных слова "file1.dat" "alt data" "local data" "summary.txt"
.
ARG
Для перебора всех параметров можно просто в скрипте написать такую конструкцию (и если ему ничего не будет будет - будет воркать):
Ключи
Ключи (options) дают возможность изменить выполнение команды. Классический идиоматический способ представления ключей в Unix/Linux - дефис/минус/тире и одна буква.
Анализ ключей
Для анализа ключей юзают встроенную команду getopts
.
Используется, обычно, связка while
+getopts
+case
, где while
юзает многократно getopts
(ибо при каждом вызове getopts
будет находить только один ключ), пока не будут получены все ключи, a case уже будет работать с переменной, которой getopts
при каждом вызове присваивает новый найденный ключ. getopts
распознает и ключи, указанные по отдельности -a -v
), и сгруппированные -av
). Можно указать, что ключ должен идти вместе с доп. аргументом -o output
или -ooutput
), указав после символа ключа двоеточие :
).
Предполагается, что ключи идут до остальных аргументов.
getopts
-у нужно передать два слова: список параметров и имя переменной, которой он присвоит очередной обнаруженный ключ.
getopts
возвращает true, когда обнаруживает ключ (дефис, за которым следует любая буква, допустимая или нет), и false
, когда дойдет до конца списка параметров; так он и работает с while
.
getopts
usage:
Строкой ':ao:v'
переданы поддерживаемые ключи. Двоеточие :
) в начале указывает команде не сообщать об ошибке при обнаружении неподдерживаемого параметра, ибо обработка таких ошибок будет реализована в case. Двоеточие :
) после o
указывает, что ключ o
должен сопровождаться доп. аргументом. А VAL
- имя переменной, куда и запишется очередной найденный ключ.
Не обнаружив ключ, getopts
присвоит переменной VAL
двоеточие :
), а переменной $OPTARG
символ ключа, для которого не был указан доп. аргумент (то есть, o
).
Обнаружив неподдерживаемый параметр, getopts
присвоит переменной VAL
знак вопроса ?
, а $OPTARG
- символ нераспознанного ключа.
Именно с помощью полученных в переменной VAL
двоеточия :
) или знака вопроса ?
) можно будет в case обработать эти ошибки:
Полный код для анализа коротких ключей:
shift
использует арифметическую операцию $((OPTIND - 1))
($OPTIND хранит индекс следующего рассматриваемого параметра) для определения кол-ва позиций, на которые нужно сдвинуть позиционные параметры. После обработки ключей, то есть, после завершения цикла while, shift
сдвигает параметры; это означает, что обработанные параметры будут удалены и следующий параметр станет первым позиционным параметром. Это нужно для исключения из дальнейшего рассмотрения всех аргументов, связанных с ключами. Неважно, каким образом будет вызван скрипт:
после выполнения shift $((OPTIND - 1))
$1 будет хранить значение file1
.
Длинные ключи
Длинные ключи начинаются с двух дефисов
--last
), иначе длинный ключ-last
был бы неотличим от сгруппированных-l -a -s -t
.
getopts
поддерживает и длинные ключи, правда, с помощью еще одного вложенного case.
Чтобы getopts
мог анализировать длинные ключи, нужно в список параметров передать ’-:’ (знак минуса и двоеточие) и еще один вложенный case для их обработки. Двоеточие нужно передать в список параметров, даже если длинный ключ не принимает аргументов.
Длинный ключ и его аргумент можно передать в скрипт двумя способами:
Анализ коротких и длинных ключей:
getopts
разрабатывалась для поддержки коротких ключей; знак минус в списке ее параметров':-:ao:v'
) позволяет двум дефисам--
) распознаваться как допустимый ключ.- Любые символы после двух дефисов
--
) будут считаться аргументом ключа и присваиваться$OPTARG
. Для сопоставления значения$OPTARG
с длинными именами ключей и нужен вложенный case. - Если аргумент длинного ключа передан со знаком равенства
--outfile=out.txt
), тоgetopts
передаст всю строку после — переменной$OPTARG
, а уже достать аргумент из этой строки можно с помощью${OPTARG#*=}
или${OPTARG#outfile=}
. - Доп. аргумент извлекается косвенно с помощью
${!OPTIND}
. Восклицательный знак!
) сообщает о косвенной ссылке, когда значение$OPTIND
используется как имя извлекаемой переменной. Например, если--output
был 3-м аргументом, в этот момент$OPTIND
будет хранить 4, и${!OPTIND}
вернет${4}
, то есть следующий аргумент с именем файла.
Справка -h
/ --help
)
Для простых скриптов подойдет такое решение:
- В качестве метки EoH (End-of-Help), можно использовать произвольную последовательность символов. Дефис
-
) нужен для автоматического удаления из строк ниже начальных табуляций (но не пробелов). Это позволяет юзать в коде отступы, которые в выводе будут проигнорированы. - Здесь используется
exit 1
, ибо скрипт не сделал ничего полезного - просто вывел справку. Можно спорить долго, но иexit 0
, иexit 1
подходят сюда (это как посмотреть).
Вообще, стоит использовать для вывода справки отдельную функцию, чтобы потом использовать его в обработчике ключей:
debug/verbose режимы вывода
По умолчанию, если юзер не запросил verbose вывод, $VERBOSE
содержит : , то есть, команду no-op (без операции) или null, которая ничего не делает, игнорирует свои аргументы и всегда возвращает true:
А если присваивается 'echo'
:
строка "$VERBOSE" 'Example verbose message...'
превратится в:
и выведет сообщение. Это простая идиома вывода сообщений по условию.
Вывод VERSION скрипта
Для больших или public скриптов возможность вывода номера версии может быть актуальной. Форматы значений $VERSION
:
Функция-обработчик аргументов
В скриптах с большим набором аргументов нужно разделять код, обрабатывающий аргументы, и основные функции. Код для анализа аргументов лучше запихнуть в функцию:
потом вызвать ее как parseargs "${@}"
- остальной код сможет теперь юзать установленные ею флаги (в этом случае имеются в виду переменные-флаги VERSION
, DEBUG
и т.д.). Это позволит отказаться от усложняющих логику кода условных конструкций для выбора между нормальным и debug/verbose режимами вывода.
Соусы:
Книга “Идиомы Bash” ⇒ Глава 8. “Аргументы”
Книга “Bash и кибербезопасность” ⇒ Глава 2. “Основы работы с bash”