我知道之前已经在Stack Overflow上问过这种性质的问题,但是即使阅读了一些线程(1)(2),我也无法获得成功。
我正在编写一个C函数,该函数将读取指定为命令行参数的文件名。但我还有空间在文件名前提供一个可选参数。
示例执行(这三个调用彼此独立):
./my_program -a foo.txt // Standalone example #1
./my_program -b foo.txt // Standalone example #2
./my_program foo.txt // Standalone example #3
我的代码:
int main(int argc, char* argv[]) {
int aflag = 0;
int bflag = 0;
int cflag = 0;
int option;
char *filename;
while ((option = getopt(argc, argv, "abc:")) != -1) {
switch (option) {
case 'a':
aflag = 1;
break;
case 'b':
bflag = 1;
break;
case 'c':
cflag = 1;
break;
default:
aflag = 1; // If no flags are set, use "a"
break;
}
}
if (argc == 2) {
filename = argv[1];
} else if (argc == 3) {
filename = argv[2];
}
printf("Flags: aflag = %d, bflag = %d, cflag = %d\n", aflag, bflag, cflag);
printf("Got filename = %s\n", filename);
此可以用于带有一个可选参数的情况。
但是,我正在阅读有关[optind]
(3)的信息,并且想知道它的正确用法是什么,以便我可以获取文件名。我似乎无法使其正常工作,并且我不知道使用这样的if
语句是否是好的样式。
例如,现在此代码仅限于一个可选参数。但是,如果我以后决定添加第二个参数怎么办?然后,我上面的代码将不起作用,因为文件名所在的argv
索引已更改。
有没有一种方法-大概使用getind
和getopt
-不管我之前指定了多少个(可选)参数,总是将最后一个参数作为文件名?
答案 0 :(得分:2)
在您链接的页面上:
如果没有更多的选项字符,则getopt()返回-1。然后optind是第一个argv元素的argv中的索引,该索引不是选项。
因此代替
REM Clear the screen
REM ------------------------------------------------------
cls
date /T >> C:\\myTemp\devTracker.log
time /T >> C:\\myTemp\devTracker.log
ECHO Starting Microsoft Visual Studio...
您只是想要
if (argc == 2) {
filename = argv[1];
} else if (argc == 3) {
filename = argv[2];
}
请注意,如果在选项之后未指定任何参数(例如,如果仅以filename = argv[optind];
的形式调用程序,则会将./my_program -a
设置为filename
,因此您应该准备好相应地进行处理。如果愿意,您还可以显式检测这种情况:
NULL
答案 1 :(得分:1)
与getopt
成为朋友时,请了解getopt
基本上会解析您的命令行,匹配选项以及需要值的选项的任何参数。所有其他非选项参数都经过重新排序,因此它们出现在参数列表的末尾。当您像通常进行检查那样循环执行时,例如while ((opt = getopt (argc, argv, "f:ohv")) != -1)
,所有不是option也不是option必需值的命令行参数将从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]
的{{1}}选项设置为下一个参数(文件名)的索引,然后在最后进行测试以确定是否检查是否将其他参数用作文件名,因为opts[0]
不再是0
。但是,如果未设置opts[0]
,则第一个非选项参数的 index 将存储在opts[0]
中。无论文件名是在"-f"
之后获取还是作为第一个非选项参数读取,您都可以调用fopen (argv[opts[0]], "r")
在main()
中打开文件。
还请注意:返回了optind
,使您可以确定是否有getopt
循环中未处理的其他(或额外)参数,因此您可以检查if (optind < argc)
返回main()
,并根据需要处理其他参数。
在一个简短的示例中(对于getopt)将其放在一起,您可以尝试使用类似以下的方法在"-f"
之后或没有"-f"
的其他任何地方传递文件名,只要它是第一个非-option参数保留,例如
#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
或{{ 1}}功能。
示例命令行可能是:
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"
会花费一些时间,这很正常,只需打开手册页并通过几个示例进行操作即可。