当您与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 需要一些时间,这很正常,只需打开手册页并通过几个示例进行操作即可。