Если скрипт имеет более одного параметра - придется проанализировать аргументы ради гарантии правильной обработки всех возможных способов передачи аргументов юзером. Да даже скрипту с одним параметром не помешает обработка параметров -h/--help.
Не стоит использовать позиционные параметры ($1, $2 и т.д.) как универсальное имя, ибо не понятно, что делает этот параметр и зачем. Лучше в начале скрипта присвоить их значения переменным с “говорящими” именами.
Простая проверка количества аргументов $#
$# — кол-во аргументов
if (($# != 2)); then echo "usage: $0 <input_file> <output_file>" exit 1fi
$@ и $*
Чтобы сослаться на все аргументы скрипта и заключить каждый из них в кавычки, можно использовать $@ (строка) или ${@} (список). $* вернет одну большую строку в кавычках со всеми аргументами. Например, при таком вызове скрипта:
$* вернет одну строку "file1.dat alt data local data summary.txt" $@ — 4 отдельных слова "file1.dat" "alt data" "local data" "summary.txt".
ARG
Для перебора всех параметров можно просто в скрипте написать такую конструкцию (и если ему ничего не будет мешать - будет воркать):
for ARG; do echo "here is an argument: $ARG"done
./args.sh bash is tuff
here is an argument: bash
here is an argument: is
here is an argument: tuff
Ключи
Ключи (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
while getopts ':ao:v' VAL ; do :; done
Строкой ':ao:v' переданы поддерживаемые ключи. Двоеточие :) в начале указывает команде не сообщать об ошибке при обнаружении неподдерживаемого параметра, ибо обработка таких ошибок будет реализована в case. Двоеточие :) послеo указывает, что ключ o должен сопровождаться доп. аргументом. А VAL - имя переменной, куда и запишется очередной найденный ключ.
Не обнаружив ключ, getopts присвоит переменной VALдвоеточие :), а переменной $OPTARG символ ключа, для которого не был указан доп. аргумент (то есть, o).
Обнаружив неподдерживаемый параметр, getopts присвоит переменной VALзнак вопроса ?, а $OPTARG - символ нераспознанного ключа.
Именно с помощью полученных в переменной VALдвоеточия :) или знака вопроса ?) можно будет в case обработать эти ошибки:
case "$VAL" in... : ) echo "Error: No arg supplied to [$OPTARG] option" ;; * ) echo "Error: unknown option [$OPTARG]" echo "Valid options are: aov" ;;...esac
Полный код для анализа коротких ключей:
while getopts ':ao:v' VAL ; do case "$VAL" in a ) AMODE=1 ;; o ) OFILE="$OPTARG" ;; v ) VERBOSE=1 ;; : ) echo "Error: No arg supplied to [$OPTARG] option" ;; * ) echo "Error: unknown option [$OPTARG]" echo "Valid options are: aov" ;; esacdoneshift $((OPTIND - 1))
shift использует арифметическую операцию $((OPTIND - 1)) ($OPTIND хранит индекс следующего рассматриваемого параметра) для определения кол-ва позиций, на которые нужно сдвинуть позиционные параметры. После обработки ключей, то есть, после завершения цикла while, shift сдвигает параметры; это означает, что обработанные параметры будут удалены и следующий параметр станет первым позиционным параметром. Это нужно для исключения из дальнейшего рассмотрения всех аргументов, связанных с ключами. Неважно, каким образом будет вызван скрипт:
script -a -o out.txt -v file1# Аргументы в bashscript file1
после выполнения shift $((OPTIND - 1))$1 будет хранить значение file1.
Длинные ключи
Длинные ключи начинаются с двух дефисов--last), иначе длинный ключ -last был бы неотличим от сгруппированных -l -a -s -t .
getopts поддерживает и длинные ключи, правда, с помощью еще одного вложенного case.
Чтобы getopts мог анализировать длинные ключи, нужно в список параметров передать ’-:’ (знак минуса и двоеточие) и еще один вложенный case для их обработки. Двоеточие нужно передать в список параметров, даже если длинный ключ не принимает аргументов.
Длинный ключ и его аргумент можно передать в скрипт двумя способами:
--outfile=out.txt--outfile out.txt
Анализ коротких и длинных ключей:
#!/usr/bin/env bashVERBOSE=":" # По дефолту включенwhile getopts ':-:ao:v' VAL ; do # (1) case a ) AMODE=1 ;; o ) OFILE="$OPTARG" ;; v ) VERBOSE=1 ;;#------------------------------------------------------------------- - ) # Этот раздел добавлен для поддержки длинных ключей (2) case "$OPTARG" in amode ) AMODE=1 ;; outfile=* ) OFILE="${OPTARG#*=}" ;; # (3) outfile ) { OFILE="${!OPTIND}"; ((OPTIND++)); } ;; # (4) verbose ) VERBOSE="echo" ;; * ) echo "unknown long argument: [$OPTARG]" exit ;; esac ;;#-------------------------------------------------------------------- : ) echo "Error: No arg supplied to [$OPTARG] option" ;; * ) echo "Error: unknown option [$OPTARG]" echo "Valid options are: aov" ;; esacdoneshift $((OPTIND - 1))# VERBOSE="echo" нужен для такого простого вывода VERBOSE-инфы:"$VERBOSE" 'Example verbose message...'
getopts разрабатывалась для поддержки коротких ключей; знак минус в списке ее параметров ':-:ao:v') позволяет двум дефисам --) распознаваться как допустимый ключ.
Любые символы после двух дефисов --) будут считаться аргументом ключа и присваиваться $OPTARG. Для сопоставления значения $OPTARG с длинными именами ключей и нужен вложенный case.
Если аргумент длинного ключа передан со знаком равенства --outfile=out.txt), то getopts передаст всю строку после — переменной $OPTARG, а уже достать аргумент из этой строки можно с помощью ${OPTARG#*=} или ${OPTARG#outfile=}.
Доп. аргумент извлекается косвенно с помощью ${!OPTIND}. Восклицательный знак !) сообщает о косвенной ссылке, когда значение $OPTIND используется как имя извлекаемой переменной. Например, если --output был 3-м аргументом, в этот момент $OPTIND будет хранить 4, и ${!OPTIND} вернет ${4}, то есть следующий аргумент с именем файла.
В качестве метки EoH (End-of-Help), можно использовать произвольную последовательность символов. Дефис -) нужен для автоматического удаления из строк ниже начальных табуляций (но не пробелов). Это позволяет юзать в коде отступы, которые в выводе будут проигнорированы.
Здесь используется exit 1, ибо скрипт не сделал ничего полезного - просто вывел справку. Можно спорить долго, но и exit 0, и exit 1 подходят сюда (это как посмотреть).
Вообще, стоит использовать для вывода справки отдельную функцию, чтобы потом использовать его в обработчике ключей:
#!/usr/bin/env bashVERBOSE=":" # По дефолту выключенwhile getopts ':-:ao:v' VAL ; do # (1) case a ) AMODE=1 ;; o ) OFILE="$OPTARG" ;; h ) { Display_help ; exit 1; } ;; v ) VERBOSE=1 ;;#------------------------------------------------------------------- - ) # Этот раздел добавлен для поддержки длинных ключей (2) case "$OPTARG" in amode ) AMODE=1 ;; outfile=* ) OFILE="${OPTARG#*=}" ;; # (3) outfile ) { OFILE="${!OPTIND}"; ((OPTIND++)); } ;; # (4) verbose ) VERBOSE="echo" ;; help ) { Display_help ; exit 1; } ;; * ) echo "unknown long argument: [$OPTARG]" exit ;; esac ;;#-------------------------------------------------------------------- : ) echo "Error: No arg supplied to [$OPTARG] option" ;; * ) echo "Error: unknown option [$OPTARG]" echo "Valid options are: aov" ;; esacdoneshift $((OPTIND - 1))
debug/verbose режимы вывода
По умолчанию, если юзер не запросил verbose вывод, $VERBOSE содержит : , то есть, команду no-op (без операции) или null, которая ничего не делает, игнорирует свои аргументы и всегда возвращает true:
и выведет сообщение. Это простая идиома вывода сообщений по условию.
Вывод VERSION скрипта
Для больших или public скриптов возможность вывода номера версии может быть актуальной. Форматы значений $VERSION:
VERSION="v1.2.3"VERSION="$PROGRAM v1.2.3"VERSION="12."VERSION=$ID$ # для CVS или SVN
Функция-обработчик аргументов
В скриптах с большим набором аргументов нужно разделять код, обрабатывающий аргументы, и основные функции. Код для анализа аргументов лучше запихнуть в функцию:
function parseargs { ...}
потом вызвать ее как parseargs "${@}" - остальной код сможет теперь юзать установленные ею флаги (в этом случае имеются в виду переменные-флагиVERSION, DEBUG и т.д.). Это позволит отказаться от усложняющих логику кода условных конструкций для выбора между нормальным и debug/verbose режимами вывода.
Соусы:
Книга Идиомы Bash→ Глава 8. Аргументы
Книга Bash и кибербезопасность→ Глава 2. Основы работы с bash