Когда вы дружите с getopt
, помните, что getopt
в основном анализирует вашу командную строку, сопоставляет параметры и любые аргументы с параметрами, требующими ввода значения.Все остальные неопционные аргументы переупорядочиваются, поэтому они появляются в конце списка аргументов.Когда вы выполняете цикл, как при обычной проверке, например, while ((opt = getopt (argc, argv, "f:ohv")) != -1)
, любые аргументы командной строки, которые не были опциями и не являлись обязательными значениями для опции, будут начинаться с argv[optind]
.Поэтому, когда ваш цикл обработки аргументов завершен, вы проверяете if (optind < argc)
, чтобы определить, есть ли у вас дополнительные доступные аргументы командной строки, которые не были обработаны в вашем цикле getopt
.
Давайте рассмотрим достаточно полный пример обработки имени файлалибо указывается после параметра "-f"
, либо просто как первый параметр без аргументов, который остается после обработки всех параметров (или мы будем читать stdin
, если не останется никаких дополнительных параметров - но обратите внимание, в этом случае вы не можетеиметь дополнительные опции, или первая будет считаться именем файла для чтения)
Один из самых простых / удобных способов обработки аргументов - просто объявить массив опций, который вы инициализируете, как все нули.Затем при обработке опций вы используете массив opts
, где каждый элемент содержит индекс соответствующей опции в argv
, либо флаг (например, установлен на 1
, если опция установлена), либо значение, полученное изпреобразование (скажем, если у вас было "-n:"
для ввода некоторого числа, то с помощью командной строки, содержащей "-n 4"
, вы можете преобразовать и сохранить фактическое значение 4
в индексе массива, связанном с параметром "-n"
(вместоиндекс argv
, который вы должны были бы преобразовать в числовое значение позже)).
Целью вашей функции processopts()
является цикл с getopt()
и полное превращение любых параметров в пригодные для использования значения для остальной части вашей программы.Используя массив опций, это облегчает передачу в качестве параметра функции для обработки всех ваших опций.Сделав тип массива опций long
, вы получите доступную собственную ширину для и strtol
преобразований, а также сможете обрабатывать как положительные, так и отрицательные значения.
Итак, давайте рассмотрим примериспользуя функцию processopts()
.В main()
или везде, где вы будете вызывать processopts()
, вы просто объявляете массив, в котором каждый элемент будет соответствовать некоторой опции, которую вы будете обрабатывать, и будете содержать значимое значение после обработки этой опции, например,
#define NOPTS 8 /* max options for sizing opts array */
...
int main (int argc, char **argv) {
long opts[NOPTS] = {0}; /* initialize opitons array all zero */
...
int optindex = processopts (argc, argv, opts); /* process all options */
Итак, выше вы объявили массив opts
и передали его вместе с argc,
и argv
в функцию processopts()
.Ваша функция processopts()
будет тогда делать что-то похожее на:
/** process command line options with getopt.
* values are made available through the 'opts' array.
* 'optind' is returned for further command line processing.
*/
int processopts (int argc, char **argv, long *opts)
{
int opt;
/* set any default values in *opts array here */
while ((opt = getopt (argc, argv, "f:ohv")) != -1) { /* getopt loop */
switch (opt) {
case 'f': /* filename */
opts[0] = optind - 1;
break;
case 'o': /* some generic option 'o' */
opts[1] = 1;
break;
case 'h': /* help */
help (EXIT_SUCCESS);
case 'v': /* show version information */
printf ("%s, version %s\n", PACKAGE, VERSION);
exit (EXIT_SUCCESS);
default : /* ? */
fprintf (stderr, "\nerror: invalid or missing option.\n");
help (EXIT_FAILURE);
}
}
/* set argv index for filename if arguments remain */
if (!opts[0] && argc > optind) opts[0] = optind++;
return optind; /* return next argument index */
}
Примечание выше, если задана опция "-f filename"
opts[0]
устанавливается в индекс следующего аргумента (имя файла), а затем тест в конце, чтобы определить, нужно ли проверять дополнительные аргументы для использования, так как имя файла пропускается, поскольку opts[0]
больше не 0
.Но если opts[0]
не было установлено, index для первого неопционального аргумента будет сохранено в opts[0]
.Независимо от того, было ли имя файла взято после "-f"
или считано в качестве первого неопционального аргумента, вы должны затем вызвать fopen (argv[opts[0]], "r")
, чтобы открыть файл в main()
.
Также обратите внимание на , чтоВозвращается optind
, позволяя вам определить, были ли дополнительные (или дополнительные) аргументы, которые не были обработаны в вашем цикле getopt
, так что вы можете проверить if (optind < argc)
обратно в main()
и обработать дополнительные аргументы, как считаете нужным.
Собрав его вместе в коротком (для getopt) примере, вы можете поэкспериментировать с чем-то вроде следующего, чтобы передать имя файла после "-f"
или где-либо еще без "-f"
, пока он не является первым.оставшийся аргумент, например
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h> /* for getopt */
#define PACKAGE "getopt_example"
#define VERSION "0.01"
#define NOPTS 8 /* max options for sizing opts array */
#define MAXC 1024 /* max characters for buffer */
int processopts (int argc, char **argv, long *opts);
void help (int xcode);
size_t rmcrlf (char *s);
int main (int argc, char **argv) {
long opts[NOPTS] = {0}; /* initialize opitons array all zero */
char buf[MAXC] = "";
size_t idx = 0;
int optindex = processopts (argc, argv, opts);
/* use filename provided as following "-f" option or provided as
* 1st non-option argument (stdin by default)
*/
FILE *fp = opts[0] ? fopen (argv[opts[0]], "r") : stdin;
if (!fp) { /* validate file open for reading */
fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
return 1;
}
/* indicate whether the option '-o' was set */
printf ("\nthe option '-o' %s set.\n\n", opts[1] ? "is" : "is not");
printf (" line : len - contents\n\n");
while (fgets (buf, MAXC, fp)) { /* read ouput length/lines from file */
size_t l = rmcrlf (buf); /* get line length, trim line ending */
printf (" %4zu : %3zu - %s\n", idx++, l, buf);
}
if (fp != stdin) /* close file if not stdin */
fclose (fp);
if (optindex < argc) /* check whether additional options remain */
printf ("\nwarning: %d options unprocessed.\n\n", argc - optindex);
for (int i = optindex; i < argc; i++) /* output unprocessed options */
printf (" %s\n", argv[i]);
return 0;
}
/** process command line options with getopt.
* values are made available through the 'opts' array.
* 'optind' is returned for further command line processing.
*/
int processopts (int argc, char **argv, long *opts)
{
int opt;
/* set any default values in *opts array here */
while ((opt = getopt (argc, argv, "f:ohv")) != -1) {
switch (opt) {
case 'f': /* filename */
opts[0] = optind - 1;
break;
case 'o': /* some generic option 'o' */
opts[1] = 1;
break;
case 'h': /* help */
help (EXIT_SUCCESS);
case 'v': /* show version information */
printf ("%s, version %s\n", PACKAGE, VERSION);
exit (EXIT_SUCCESS);
default : /* ? */
fprintf (stderr, "\nerror: invalid or missing option.\n");
help (EXIT_FAILURE);
}
}
/* set argv index for filename if arguments remain */
if (!opts[0] && argc > optind) opts[0] = optind++;
return optind; /* return next argument index */
}
/** display help */
void help (int xcode)
{
xcode = xcode ? xcode : 0;
printf ("\n %s, version %s\n\n"
" usage: %s [-hv -f file (stdin)] [file]\n\n"
" Reads each line from file, and writes line, length and contents\n"
" to stdout.\n\n"
" Options:\n\n"
" -f file specifies filename to read.\n"
" (note: file can be specified with or without -f option)\n"
" -o generic option for example.\n"
" -h display this help.\n"
" -v display version information.\n\n",
PACKAGE, VERSION, PACKAGE);
exit (xcode);
}
/** remove newline or carriage-return from 's'.
* returns new length on success, -1 of 's' is NULL.
*/
size_t rmcrlf (char *s)
{
size_t len;
if (!s) return 0; /* validate s not NULL */
s[(len = strcspn (s, "\r\n"))] = 0; /* nul-terminate saving len */
return len; /* return len */
}
(программа сообщит вам, если установлена опция "-o"
"is"
или "is not"
, а затем просто прочитает имя файла, найденное в аргументах командной строки (или stdin
, если не указано имя файла или дополнительный аргумент)и выплевывает индекс строки (0 - N-1), длину строки и, наконец, саму строку, за которой следуют любые дополнительные аргументы, не обработанные getopt
или функцией processopts()
.
ПримерКомандная строка может быть:
$ ./bin/getopt_min -f dat/captnjack.txt extra1 extra2
(прочитать файл dat/captnjack.txt
и показать, что два дополнительных аргумента не обработаны)
$ ./bin/getopt_min dat/captnjack.txt -o extra1 extra2
(то же самое)
$ ./bin/getopt_min -o <dat/captnjack.txt
(файл читается на stdin
)
Наконец, опции "-h"
и "-v"
просто приводят к отображению справки или информации о версии.
Просмотрите все и дайте мне знать, еслиу вас есть вопросы. Требуется некоторое время, чтобы переварить getopt
, это нормально, просто держите страницу руководства открытой и проработайте пару примеров.