Привет. Я в свободное время для рабочих целей пишу один проект, который состоит из кучи bash-скриптов. Есть одна точка входа, которая из cli принимает аргументы. Этот скрипт подключает остальные куски функционала через source. В этих кусках описаны функции, которые должны обрабатывать разные аргументы, нужные только для чего-то конкретного.
В первой версии проекта я просто явно передавал $1, $2, $N
сверху вниз, в другие функции. Это было топорным решением, и оно работало, потому что возможные аргументы и их порядок был известен и прост. Но эстетически это было отвратительно. Чтобы мне было приятно смотреть на код, нужно было сильно усложнить вообще всё, чтобы всё стало намного проще.
Итак, я должен принимать от пользователя разные аргументы в разные функции, при этом я хочу дать пользователю возможность передать на вход:
- длинные аргументы без значений:
./script.sh --foo --bar
- длинные аргументы со значениями:
./script.sh --foo=bar
- короткие аргументы без значений:
./script.sh -a -b -c
- короткие аргументы со значениями:
./script.sh -a avalue -b bvalue
- комбинировать короткие любые короткие аргументы в одно слово:
./script.sh -abc bvalue
- всё перечисленное выше — одновременно!
- порядок аргументов не должен иметь значения
- иметь доступ к любому из них откуда угодно в любой момент времени.
getopt мог бы решить эту проблему. Но внутри каждой отдельной функции невозможно парсить разные аргументы:
#!/bin/bash
# все возможные аргументы надо перечислять здесь
getopt -o a -- $@
func1() {
getopt -o b -- $@ # вызовет ошибку
}
func2() {
getopt -o с -- $@ # вызовет ошибку
}
getopts просто не поддерживает длинные аргументы.
Всегда была возможность получить нужные аргументы по индексам и передать дальше, и ранее я так и делал — и это то, от чего хочу уйти.
Любой скрипт, запускаемый из cli, даёт тебе доступ к массиву аргументов, с которыми он был вызван, через $@
. Моё дело только грамотно его распарсить.
В двух словах, я сделал так:
- для коротких аргументов: бегу в цикле по $@ и проверяю ожидаемый ли это аргумент (-b) или находится ли он среди комбинации других коротких (-abc). Если это он и ожидается факт его наличия, то возвращаю 1. Если это он и ожидается его значение, то возвращаю следующий аргумент;
- для длинных аргументов то же самое, но проще, потому как не приходится проверять комбинации: если мы нашли аргумент и требуем только его существование, то возвращаем 1, а если нашли аргумент со значением — возвращаем все от
=
до конца аргумента.
Ещё написал функцию для получения аргумента по его индексу — тоже может быть полезно, если заведомо известен порядок аргументов и тебе нужно взять какой-то конкретный.
Ниже — готовый код, с примерами и более подробными комментариями на английском пошагово. Ты можешь сохранить этот код в файл args.sh
, а там, где тебе нужно, дописать . args.sh
или source args.sh
и использовать функции arg
, argl
и argn
как показано на примерах.