15. Разбор аргументов программы#
Под аргументами командной строки мы подразумеваем данные, передаваемые запускаемой программе из командной строки. Аргументы разделяются друг от друга пробелами или табуляцией. Имя программы составляет первый и обязательный аргумент.
user@host:~$ ls -rhl --all --sort size ~
Вывод на экран файлов и каталогов (команда ls) и скрытого содержимого (опция --all) домашнего каталога (операнд ~) с подробной информацией (опция -l), отсортированных по размеру (опция --sort size) в обартном порядке (опция -r) в удобном для восприятия формате размера (опция -h).
Как видим, аргументы командной строки не совпадают с опциями. Аргумент содежит одновременно 3 опции (-rhl) или только опцию (–sort), но без ее значения (size). Нужна дополнительная обработка данных, которая бы разобрала и извлекла опции, значения опций и операнды программы.
Каждая утилита UNIX начинает работу с разбора аргументов командной строки. Чтобы упростить эту работу и прийти к единому соглашению, было решено реализовать общие функции разбора. Для разбора параметров командной строки воспользуемся существующим решением – функциями getopt и getopt_long и набором глобальных переменных optarg, optind, opterr, optopt. Функции входят в состав ОС UNIX в виде библиотеки Си. Справочная информация man 3 getopt.
Программа на Си начинается с главной функции:
int main(int argc, char** argv)
Функция на вход принимает два аргумента:
argc, количество переданных в программу аргументов;argv, список аргументов.
#include <stdio.h>
int main(int argc, char** argv)
{
int i;
for(i = 0; i < argc; ++i)
printf("argv[%d] = %s\n", i, argv[i]);
return 0;
}
Разбор коротких опций#
Функция getopt разбирает аргументы командной строки на короткие опции.
При этом длинные опции она не поддерживает.
int getopt(int argc, char* const argv[], const char* optstring)
Первые два аргумента функции, argc и argv, определяют количество и массив аргументов командной строки.
Они поставляются главной функцией main программы.
Третий аргумент optstring задает список всех допустимых опций в виде строки.
При каждом вызове функции getopt извлекает из аргументов командной строки следующую опцию и ее значение (если она задана).
Если разбор опций заканчивается, то функция возвращает -1.
Цикл разбора будет выглядеть как:
#include <stdio.h>
#include <unistd.h>
int main(int argc, char** argv)
{
while(1)
{
int c = getopt(argc, argv, "ab:c::");
if(c == -1)
break;
}
return 0;
}
Формат optstring следующий.
В ней перечислены буквенные символы опций.
Если опция принимает значение, то после нее ставят символ :.
Если опция принимает значение, но оно необязательно, то ставят два символа – ::.
В примере выше определены три опции -a, -b, -c.
Опция -a без параметра, опция -b с обязательным параметром, а опция -c может иметь или не иметь параметр.
Функция getopt возрвщает символ разобранной опции, а ее значение (если оно задано), заносится в глобальную переменную optarg.
Если значение на задано, то optarg=0.
#include <stdio.h>
#include <unistd.h>
int main(int argc, char** argv)
{
while(1)
{
int c = getopt(argc, argv, "ab:c::");
if(c == -1)
break;
switch(c)
{
case 'a':
break;
case 'b':
break;
case 'c':
break;
};
}
return 0;
}
Если во время разбора встретится опция, которой нет в списке optstring, то функция getopt вернет символ ?, а символ опции заносится в optopt.
Сообщение об ошибке разбора выведется в stderr.
Чтобы заблокировать вывод сообщения об ошибке, глобальная переменная opterr устанавливается в 0.
#include <stdio.h>
#include <unistd.h>
int main(int argc, char** argv)
{
opterr = 1;
while(1)
{
int c = getopt(argc, argv, "ab:c::");
if(c == -1)
break;
switch(c)
{
case '?':
fprintf(stderr, "Try \"%s\" with options -a, -bParam or -c (-cParam)\n", argv[0]);
return 1;
case 'a':
break;
case 'b':
break;
case 'c':
break;
};
}
return 0;
}
При повторном вызове функции getopt нужно знать, где она остановила разбор.
Это состояние хранится в глобальной переменной optind – индекс необработанного аргумента в массиве argv.
В самом начале ей назначена 1.
Если понадобиться разобрать заново аргументы в строке, optind устанавливают в 1.
Когда разбор опций заканчивается, т.е. функция getopt возвращает -1, а optind указывает на первый аргумент, который не относится к опциям.
За опциями команды следуют параметры команды.
Чтобы их извлечь, воспользуемся кодом:
while(optind < argc)
printf("operand = %s\n", argv[optind++]);
Разбор длинных опций#
Функция getopt_long разбирает не только короткие, но и длинные опции.
Она ведет себя также, как и getopt.
int getopt_long(int argc, char* const argv[], const char* optstring, const struct option* longopts, int* longindex)
Первые три параметра аналогичны функции getopt.
Остальные два аргумента связаны с длинными опциями.
Рассмотрим их подробнее.
Аргумент longopts содержит описание всех допустимых длинных опций – таблицей длинных опций.
Это описание задается массивом записей типа option, объявленного в заголовочном файле getopt.h:
struct option
{
const char* name;
int has_arg;
int* flag;
int val;
};
name, имя длинной опции.has_arg, признак присутствия аргумента у опции. Три возможных значения, которые заданы именованными константами: без аргумента (no_argument), с аргументом (required_argument) или с необязательным аргументов (optional_argument).flag, указывает на переменную, которую необходимо проинициализировать при встрече с опцией. Переменная инициализируется значением поляval. Еслиflagне задан, то функцияgetopt_longвозвращает значениеval.val, значение, ассоциированное с опцией.
Последний аргумент массива longopts инициализируется структурой option, заполненной нулями.
Аргумент longindex, если он задан, на выходе инициализируется индексом разобранной опции в структуре longopts.
Функция возвращает для длинных опций поле val из структуры option или 0, если задано после flag, или символ короктой опции.
-1, если разор опций закончился или ? если встретились с неизвестной опцией или с опцией без параметра.
Вопросы для самоконтроля#
Пусть программа
programвызвана с тремя опциями-a,-b,-cследующим образом:./program -abc. Функцияgetoptвызовется три раза, пока не вернет -1. Но при первом и втором его вызове глобальная переменнаяoptindостанется равным 1. Каким образом функция узнает, откуда начать разбор.Как передать программе один составной аргумент
hello world? Тогда как./program hello worldинтерпретируется как два аргумента.