Шаг 10 - Передача опций в программу - getopt

Почти всем сложным программам для работы требуется входные параметры (опции, аргументы - называйте как хотите), от значения которых строится последовательность работы алгоритма заложенного в программе или используются различные источники данных.

Вы наверняка знаете, что передача параметров в программу на C/C++ осуществляется через массив функции main(). Так повелось, что он называется argv (от arguments values - значения аргументов), но в принципе его можно назвать и по другому. Количество этих параметров передается через переменную argc (от arguments counter - счетчик аргументов).

Программа, для работы которой требуется набор входных параметров задается при помощи специального определения функции main():

int main(int argc, char *argv[]{
};

int main(int argc, char **argv){
};

Давайте напишем маленькую программку, которая выводит значения переданных параметров:

// программа test.c

#include <stdio.h>
int main(int argc, char *argv[]){
	int i=0;
	for (i=0;i<argc;i++){
		printf("Argument %d: %s\n",i,argv[i]);
	};
};

Сохраняем в файл test.c и компилируем:

dron:~# gcc test.c -o test

После этого попробуем запустить программу:

dron:~# ./test
Argument 0: ./test

Передадим несколько параметров:

dron:~# ./test qwe sdf fgh hjk kl 123 --help
Argument 0: ./test
Argument 1: qwe
Argument 2: sdf
Argument 3: fgh
Argument 4: hjk
Argument 5: kl
Argument 6: 123
Argument 7: --help

В качестве первого параметра программе всегда передается ее имя и таким образом программа может узнать свое название, т.е. имя файла, в котором она содержится.

Но моя цель не говорить о том, как передаются параметры, а как с ними работать. Для начала надо вспомнить, что в системе Linux существует два вида параметров: короткие и длинные. Короткие параметры начинаются с одного дефиса и имеют длину в один символ, их просто и быстро набирать в командной строке. Длинные параметры начинаются с двух дефисов и могут иметь длинное имя, которое целесообразно использовать в скриптах (чтобы потом можно было вспомнить, что и как происходит). Кроме этого любой параметр может иметь значение, а может и не иметь. Приведу для примера несколько параметров:

-h       - короткий параметр
--help   - длинный параметр

-s 10    - параметры со значениями
--size 10
--size=10

Так вот, существует несколько специальных функций предназначенных для разбора списка переданных параметров:

Давайте разберемся с работой первой функции - getopt(...). Ее определение выглядит следующим образом:

#include <unistd.h>

int getopt(int argc, char * const argv[],
	const char *optstring);

extern char *optarg;
extern int optind, opterr, optopt;

Эта функция последовательно перебирает переданные параметры в программу. Для работы в функцию передается количество параметров argc, массив параметров argv[] и специальная строка optstring, в которой перечисляются названия коротких параметров и признаки того, что параметры должны иметь значение. Например, если программа должна воспринимать три параметра a, b, F , то такая строка бы выглядела как "abF". Если параметр должен иметь значение, то после буквы параметра ставится двоеточие, например параметр F и d имеют значения, а параметры e, a и b не имеют, тогда эта строка могла бы выглядеть как "eF:ad:b". Если параметр может иметь (т.е. может и не иметь) значение, то тогда ставится два знака двоеточия, например "a::" (это специальное расширение GNU). Если optstring содержит "W:", то тогда параметр -W opt переданный в программу, будет восприниматься как длинный параметр --opt. Это связано с тем, что параметр W зарезервирован в POSIX.2 для расширения возможностей.

Для перебора параметров функцию getopt() надо вызывать в цикле. В качестве результата возвращется буква названия параметра, если же параметры кончились, то функция возвращает -1. Индекс текущего параметра хранится в optind, а значение параметра помещается в optarg (указатель просто указывает на элемент массива argv[]). Если функция находит параметр не перечисленный в списке, то выводится сообщение об ошибке в stderr и код ошибки сохраняется в opterr, при этом в качестве значения возврящается "?". Вывод ошибки можно запретить, если установить opterr в 0.

#include <stdio.h>
#include <unistd.h>

int main(int argc, char *argv[]){
    int rez=0;

//	opterr=0;
	while ( (rez = getopt(argc,argv,"ab:C::d")) != -1){
		switch (rez){
		case 'a': printf("found argument \"a\".\n"); break;
		case 'b': printf("found argument \"b = %s\".\n",optarg); break;
		case 'C': printf("found argument \"C = %s\".\n",optarg); break;
		case 'd': printf("found argument \"d\"\n"); break;
		case '?': printf("Error found !\n");break;
        };
	};

};

Попробуем скомпилировать данную программку и запустить:

dron:~# gcc test.c -o test

dron:~# ./test -a -b -d -C
found argument "a".
found argument "b = -d".
found argument "C = (null)".

dron:~# ./test -a -b -C -d
found argument "a".
found argument "b = -C".
found argument "d"

dron:~# ./test -a -b1 -C -d
found argument "a".
found argument "b = 1".
found argument "C = (null)".
found argument "d"

dron:~# ./test -b1 -b2 -b 15
found argument "b = 1".
found argument "b = 2".
found argument "b = 15".

Давайте посмотрим, как функция getopt вылавливает ошибки. Попробуем задать параметр, которого нет в списке:

dron:~# ./test -h -a
./test: invalid option -- h
Error found !
found argument "a".

Как я и говорил, функция вывела сообщение об ошибке в stderr. Давайте выключим вывод сообщений, для этого надо где-то в программе перед вызовом функции вставить opterr=0;. Компилируем и запускаем:

dron:~# ./test -h -a
Error found !
found argument "a".

Теперь, как видите, сообщение больше не выдается, зато как и раньше можно обработать ошибку самому.


Предыдущий Шаг | Следующий Шаг | Оглавление
Автор Кузин Андрей.