Парсинг длинных параметров командной строки достаточно сложный процесс, поэтому бибилиотека GNU C Library имеет специальную функцию getopt_long(), которая может работать одновременно и с длинными и с короткими параметрами. Для работы только с длинными именами параметров существует функция getopt_long_only.
Для того, чтобы работать с этими функциями Вам потребуется подключить файл getopt.h. Выглядят эти функции следующим образом:
#define _GNU_SOURCE #include <getopt.h> int getopt_long(int argc, char * const argv[], const char *optstring, const struct option *longopts, int *longindex); int getopt_long_only(int argc, char * const argv[], const char *optstring, const struct option *longopts, int *longindex);
Для работы функции getopt_long ей нужны следующие данные:
Основным отличием этих фукнций от getopt является потребность в специальном массиве. О нем и поговорим. Массив longopts состоит из записей struct option имеющих следующий вид:
struct option { const char *name; int has_arg; int *flag; int val; };
В первом поле name задается название длинного параметра.
Поле has_arg определяет нужно ли для этого параметра значение. Для этого в getopt.h определены специальные значения:
#define no_argument 0 #define required_argument 1 #define optional_argument 2
Как видите, если значение has_arg равно 0 (no_argument), то параметр не должен иметь значение, если 1 (required_argument), то параметр должен иметь значение. Если же значение для параметра опционально, то has_arg равен 3 (optional_argument).
Поле flag задает указатель на флаг, в который помещается значение val, если найден данный параметр (сама функция при этом возвращает 0). Если указатель равен NULL, то функция возвращает значение val в качестве результата работы.
Поле var содержит значение, которое помещается в flag или возвращается в качестве результата работы функции.
Последняя запись массива longopts должна содержать нулевые значения, для того чтобы функция могла однозначно определить конец массива.
Использовать данную функцию можно несколькими способами. Первый способ самый простой, и заключается в установке флажков программы с помощью данной функции в зависимости от входных параметров.
Давайте посмотрим пример longopt1.c:
#include <stdlib.h> #include <stdio.h> #include <getopt.h> int main (int argc, char *argv[]) { int flag_a = 0; int flag_b = 0; int flag_c = 0; const char* short_options = "abc"; const struct option long_options[] = { { "opta", no_argument, &flag_a, 1 }, { "optb", no_argument, &flag_b, 10 }, { "optc", no_argument, &flag_c, -121 }, { NULL, 0, NULL, 0} }; while (getopt_long(argc, argv, short_options, long_options, NULL)!=-1); printf("flag_a = %d\n",flag_a); printf("flag_b = %d\n",flag_b); printf("flag_c = %d\n",flag_c); printf("\n"); };
После компиляции gcc longopt1.c -o longopt1 получим программу. Вот некоторые результаты работы:
dron~# ./longopt1 flag_a = 0 flag_b = 0 flag_c = 0 dron~# ./longopt1 --opta flag_a = 1 flag_b = 0 flag_c = 0 dron~# ./longopt1 --optb --optc flag_a = 0 flag_b = 10 flag_c = -121 dron~# ./longopt1 -a -b -c flag_a = 0 flag_b = 0 flag_c = 0
Как видите, когда функция увидела параметры --opta, --optb или --optc она сразу же установила переменные flag_a, flag_b и flag_с значениями, которые были указаны в массиве long_options. Но посмотрите на короткие параметры -a, -b и -c. Они не были задействованы. А все от того, что в качестве результата работы функция возвращает:
Мы с вами обработку коротких параметров не предусмотрели, если Вы сейчас модифицируете код, то сможете увидеть и их:
// добавьте переменную int rez; // новый цикл обработки параметров while ((rez=getopt_long(argc,argv,short_options, long_options,NULL))!=-1) { printf("rez: %d = \'%c\'\n",rez,rez); };
Если сейчас запустить программу, то она выдаст следующее:
dron~# ./longopt1 -abc rez: 97 = 'a' rez: 98 = 'b' rez: 99 = 'c' flag_a = 0 flag_b = 0 flag_c = 0 dron~# ./longopt1 -a -c -g rez: 97 = 'a' rez: 99 = 'c' ./a.out: invalid option -- g rez: 63 = '?' flag_a = 0 flag_b = 0 flag_c = 0
Теперь можно обрабатывать и короткие параметры, а чтобы это делать все сразу, существует как раз второй метод использования этой функции. Это когда указатели flag устанавливают в NULL, а значения val устанавливают в названия коротких параметров. При этом вся обработка результатов происходит в switch структуре. Давайте попробуем создать файл longopt2.c:
#include <stdlib.h> #include <stdio.h> #include <getopt.h> int main (int argc, char *argv[]){ const char* short_options = "hs::f:"; const struct option long_options[] = { { "help", no_argument, NULL, 'h' }, { "size", optional_argument, NULL, 's' }, { "file", required_argument, NULL, 'f' }, { NULL, 0, NULL, 0 } }; int rez; int option_index; while ((rez=getopt_long(argc,argv,short_options, long_options,&option_index))!=-1){ switch(rez){ case 'h': { printf("This is demo help. Try -h or --help.\n"); printf("option_index = %d (\"%s\",%d,%c)\n", option_index, long_options[option_index].name, long_options[option_index].has_arg, long_options[option_index].val ); break; }; case 's': { if (optarg!=NULL) printf("found size with value %s\n",optarg); else printf("found size without value\n"); break; }; case 'f': { printf("file = %s\n",optarg); break; }; case '?': default: { printf("found unknown option\n"); break; }; }; }; return 0; };
Теперь посмотрите на работу программы. Попробуем параметр --help и -h.
dron~# ./longopt2 --help This is demo help. Try -h or --help. option_index = 0 ("help",0,h) dron~# ./longopt2 -h This is demo help. Try -h or --help. Segmentation fault
В первом случае все удачно, вывелась помощь и значение option_index. Во втором случае программа "упала" с сообщением об ошибке. Почему ? Ошибка Segmentation fault выдается когда программа пытается работать с неверными указателями. А в нашем случае мы пытаемся получить по option_index название параметра. В случае когда найден короткий параметр значение option_index не определено. Что же делать ?! А все просто. Модифицируем код чуток:
int option_index = -1; //обнулим в начале (установим признак ошибки) while (...){ switch(...){ }; option_index = -1; // снова делаем ошибку };
При такой работе option_index можно применять для определения типа переданного параметра. Если он был длинным, то это значение будет больше нуля и равно порядковому номеру параметра в массиве. Если же -1, то это значит, что параметр короткий:
if (option_index < 0) printf("short help option\n"); else printf("option_index = %d (\"%s\",%d,%c)\n", option_index, long_options[option_index].name, long_options[option_index].has_arg, long_options[option_index].val );
Теперь все работает:
dron~# ./longopt2 --help This is demo help. Try -h or --help. option_index = 0 ("help",0,h) dron~# ./longopt2 -h This is demo help. Try -h or --help. short help option
Но это еще не все фокусы :) Попробуйте поиграть с параметрами size и file:
dron~# ./longopt2 -s 10 found size without value dron~# ./longopt2 -s10 found size with value 10 dron~# ./longopt2 --size 10 found size without value dron~# ./longopt2 --size=10 found size with value 10 dron~# ./longopt2 -f asd file = asd dron~# ./longopt2 -fasd file = asd dron~# ./longopt2 --file asd file = asd dron~# ./longopt2 --file=asd file = asd
Как видите не все так просто. У нас size задан в массиве как optional_argument, т.е. параметр с опциональным аргументом. И получается, что когда нет явного признака присвоения, т.е. когда в коротком виде значение не стоит рядом с названием, а в длинном виде нет знака "=", наш параметр не получает никакого значения.
А вот параметр file заданный как required_argument получает свое значение в любом случае. И в этом заключается отличие типов аргументов. Теперь при разработке программ надо всегда учитывать, что если вы используете опциональный аргумент, то пользователь может ошибочно ввести командную строку, и вместо значения введенного пользователем Ваша программа будет использовать значение, которое вы используете по умолчанию. Незнаю, помоему достаточно значительная проблема, о которой следует помнить.
Осталось лишь сказать о функции getopt_long_only( ), которая является полным аналогом getopt_long() за исключением того, что даже короткие параметры она пытается сравнить с длинными. Модифицируйте название функции и попробуйте запустить программу:
dron~# ./longopt3 -f 10 file = 10 dron~# ./longopt3 -f10 file = 10 dron~# ./longopt3 --file=10 file = 10 dron~# ./longopt3 -file 10 file = 10 dron~# ./longopt3 -fil 100 file = 100
Вот так вот. Данная возможность может являться и плюсом и минусом. Полезной она оказывается, когда пользователь ошибочно набирает строку и указывает вместо --opt параметр -opt. Но эта функция может сыграть злую шутку, если вдруг из коротких названий параметров получится название длинного параметра и вместо перечисления '-size' = '-s -i -z -e' у Вас получится название длинного параметра --size от -s. Вообще работа данной функции несколько загадочна и неоднозначна, поэтому сами попробуйте ее в действии. Я же думаю, что в программах со сложными входными параметрами лучше воздержаться от ее использования. Зато в программах с небольшим количеством параметров эта функция может позволить игнорировать ошибки пользователя.
Будьте бдительны и Ваши программы будут работать без ошибок !!!