我们在linux下使用各种命令的时候经常会输入各种选项,长选项或短选项,比如运行一个软件的时候,使用-help命令后在终端会提示各种操作选项。
本文地址:http://wuyudong.com/2016/11/23/3101.html,转载请注明出处。
在Linux中,我们可以使用getopt、getopt_long、getopt_long_only来对这个问题进行处理。它们的原型如下:
#include <unistd.h> int getopt(int argc, char *const argv[], const char *optstring); extern char *optarg; extern int optind, opterr, optopt; #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讲起:
int getopt(int argc, char * const argv[], const char *optstring);
getopt函数的前两个参数,就是main函数的argc和argv,这两者直接传入即可,要考虑的就只剩下第三个参数。
#include <unistd.h> #include <stdlib.h> #include <stdio.h> int main(int argc, char *argv[]) { int opt; char *optstring = "a:b:c:d"; while ((opt = getopt(argc, argv, optstring)) != -1) { printf("opt = %c\n", opt); printf("optarg = %s\n", optarg); printf("optind = %d\n", optind); printf("argv[optind - 1] = %s\n\n", argv[optind - 1]); } return 0; }
编译运行:
wu@ubuntu:~/linuxc$ ./getopts -a 100 -b 200 -c admin -d
opt = a
optarg = 100
optind = 3
argv[optind – 1] = 100
opt = b
optarg = 200
optind = 5
argv[optind – 1] = 200
opt = c
optarg = admin
optind = 7
argv[optind – 1] = admin
opt = d
optarg = (null)
optind = 8
argv[optind – 1] = -d
char *argv[] = {“getopts”, “-a”, “100”, “-b”, “200”, “-c”, “admin”, “-d”};
index: 0 1 2 3 4 5 6 7
optstring的格式举例说明比较方便,例如:
char *optstring = “abcd:”;
上面这个optstring在传入之后,getopt函数将依次检查命令行是否指定了 -a, -b, -c及 -d(这需要多次调用getopt函数,直到其返回-1),当检查到上面某一个参数被指定时,函数会返回被指定的参数名称(即该字母)
最后一个参数d后面带有冒号,: 表示参数d是可以指定值的,如 -d 100 或 -d user。
当处理一个带参数的选项时,全局变量optarg会指向它的参数
当函数分析完所有参数时,全局变量optind(into argv)会指向第一个‘非选项’的位置,例如上面的代码最后,opind=8
optind表示的是下一个将被处理到的参数在argv中的下标值。
如果指定opterr = 0的话,在getopt、getopt_long、getopt_long_only遇到错误将不会输出错误信息到标准输出流。
下面来讲getopt_long函数,getopt_long函数包含了getopt函数的功能,并且还可以指定“长参数”(或者说长选项),与getopt函数对比,getopt_long比其多了两个参数:
const struct option *longopts, int *longindex
getopt被用来解析命令行选项参数。getopt_long支持长选项的命令行解析,getopt_long是GNU C的一个常用函数,使用man getopt_long,得到其声明如下:
int getopt_long(int argc, char * const argv[],const char *optstring, const struct option *longopts,int *longindex);
下面介绍各个参数的含义:
函数中的argc和argv通常直接从main()的两个参数传递而来。optsting是选项参数组成的字符串:
字符串optstring可以下列元素:
1. 单个字符,表示选项,
2. 单个字符后接一个冒号:表示该选项后必须跟一个参数。参数紧跟在选项后或者以空格隔开。该参数的指针赋给optarg。
3. 单个字符后跟两个冒号,表示该选项后可以有参数也可以没有参数。如果有参数,参数必须紧跟在选项后不能以空格隔开。该参数的指针赋给optarg。(这个特性是GNU的扩张)。
optstring是一个字符串,表示可以接受的参数。例如,”a:b:cd”,表示可以接受的参数是a,b,c,d,其中,a和b参数后面跟有更多的参数值。(例如:-a host -b name)
参数longopts,其实是一个结构的实例:
struct option { const char *name; //name表示的是长参数名 int has_arg ; //has_arg表明是否跟参数值 int *flag; // int val; //和flag联合决定返回值 };
参数has_arg有3个值:
no_argument(或者是0),表示该参数后面不跟参数值
required_argument(或者是1),表示该参数后面一定要跟个参数值
optional_argument(或者是2),表示该参数后面可以跟,也可以不跟参数值
参数flag:如果flag是NULL(通常情况),则函数会返回与该项option匹配的val值;如果flag不是NULL,则将val值赋予flag所指向的整型数中,并且返回值设置为0。
参数val:用于指定函数找到该选项时的返回值,或者当flag非空时指定flag指向的数据的值。
参数longindex,一般赋为NULL即可;如果longindex非空,它指向的变量将记录当前找到参数符合longopts里的第几个元素的描述,即是longopts的下标值。
下面写个程序练习一下:
#include <stdio.h> #include <getopt.h> char *l_opt_arg; char* const short_options = "ngl:"; struct option long_options[] = { { "name", 0, NULL, 'n' }, { "gf_name", 0, NULL, 'g' }, { "love", 1, NULL, 'l' }, { NULL, 0, NULL, 0 }, }; int main(int argc, char *argv[]) { int opt; int option_index = 0; while((opt = getopt_long (argc, argv, short_options, long_options, &option_index)) != -1) { switch (opt) { case 'n': printf("My name is WYD.\n"); printf("opt = %c\n", opt); printf("optarg = %s\n", optarg); printf("optind = %d\n", optind); printf("argv[optind - 1] = %s\n", argv[optind - 1]); printf("--------------------\n"); break; case 'g': printf("She name is HZJ.\n"); printf("opt = %c\n", opt); printf("optarg = %s\n", optarg); printf("optind = %d\n", optind); printf("argv[optind - 1] = %s\n", argv[optind - 1]); printf("--------------------\n"); break; case 'l': l_opt_arg = optarg; printf("Our love is %s!\n", l_opt_arg); printf("opt = %c\n", opt); printf("optarg = %s\n", optarg); printf("optind = %d\n", optind); printf("argv[optind - 1] = %s\n", argv[optind - 1]); printf("--------------------\n"); break; } } return 0; }
编译运行:
wu@ubuntu:~/linuxc$ gcc getopt_long.c -o getopt_long
wu@ubuntu:~/linuxc$ ./getopt_long -n -g -l forever
My name is WYD.
opt = n
optarg = (null)
optind = 2
argv[optind – 1] = -n
——————–
She name is HZJ.
opt = g
optarg = (null)
optind = 3
argv[optind – 1] = -g
——————–
Our love is forever!
opt = l
optarg = forever
optind = 5
argv[optind – 1] = forever
——————–
linux 命令行约定:
几乎所有的GNU/Linux程序都遵循一些命令行参数定义的约定。程序希望出现的参数可以分成两种:选项(options or flags)、其他类型的的参数。Options修饰了程序运行的方式,其他类型的参数则提供了输入(例如,输入文件的名称)。
对于options类型参数可以有两种方式:
1)短选项(short options):顾名思义,就是短小参数。它们通常包含一个连字号和一个字母(大写或小写字母)。例如:-s,-h等。
2)长选项(long options):长选项,包含了两个连字号和一些大小写字母组成的单词。例如,–size,–help等。
*注:一个程序通常会提供包括short options和long options两种参数形式的参数。
对于其他类型参数的说明:
这种类型的参数,通常跟随在options类型参数之后。例如,ls –s /功能为显示root目录的大小。’/’这个参数告诉ls要显示目录的路径。
getopt_long()函数使用规则:
(1)使用前准备两种数据结构
字符指针型变量
该数据结构包括了所有要定义的短选项,每一个选项都只用单个字母表示。如果该选项需要参数(如,需要文件路径等),则其后跟一个冒号。例如,11短选项分别为9, 1, 2, V, f, r, t, p, c, ?, h,其中-t, -p, -c需要参数,其他不需要参数。那么,我们可以将数据结构定义成如下形式:
const char * const shor_options = “912Vfrt:p:c:?h” ;
struct option 类型数组
该数据结构中的每个元素对应了一个长选项,并且每个元素是由四个域组成。具体参见上面。另外,数据结构的最后一个元素,要求所有域的内容均为0,即{NULL, 0, NULL,0}。下面举例说明(本文的例子来自webbench):
static const struct option long_options[]= { {"force",no_argument,&force,1}, {"reload",no_argument,&force_reload,1}, {"time",required_argument,NULL,'t'}, {"help",no_argument,NULL,'?'}, {"http09",no_argument,NULL,'9'}, {"http10",no_argument,NULL,'1'}, {"http11",no_argument,NULL,'2'}, {"get",no_argument,&method,METHOD_GET}, {"head",no_argument,&method,METHOD_HEAD}, {"options",no_argument,&method,METHOD_OPTIONS}, {"trace",no_argument,&method,METHOD_TRACE}, {"version",no_argument,NULL,'V'}, {"proxy",required_argument,NULL,'p'}, {"clients",required_argument,NULL,'c'}, {NULL,0,NULL,0} };
(2)调用方法
参照(1)准备的两个数据结构,则调用方式可为:
getopt_long( argc, argv, short_options, long_options, NULL);
(3)几种常见返回值
(a)每次调用该函数,它都会分析一个选项,并且返回它的短选项,如果分析完毕,即已经没有选项了,则会返回-1。
(b)如果getopt_long()在分析选项时,遇到一个没有定义过的选项,则返回值为‘?’,此时,程序员可以打印出所定义命令行的使用信息给用户。
(c)当处理一个带参数的选项时,全局变量optarg会指向它的参数
(d)当函数分析完所有参数时,全局变量optind(into argv)会指向第一个‘非选项’的位置
在执行getopt_long函数之前,可以先在终端打印一些help信息,比如定义一个函数:
static void usage(void) { fprintf(stderr, "webbench [option]... URL\n" " -f|--force Don't wait for reply from server.\n" " -r|--reload Send reload request - Pragma: no-cache.\n" " -t|--time <sec> Run benchmark for <sec> seconds. Default 30.\n" " -p|--proxy <server:port> Use proxy server for request.\n" " -c|--clients <n> Run <n> HTTP clients at once. Default one.\n" " -9|--http09 Use HTTP/0.9 style requests.\n" " -1|--http10 Use HTTP/1.0 protocol.\n" " -2|--http11 Use HTTP/1.1 protocol.\n" " --get Use GET request method.\n" " --head Use HEAD request method.\n" " --options Use OPTIONS request method.\n" " --trace Use TRACE request method.\n" " -?|-h|--help This information.\n" " -V|--version Display program version.\n" ); };
然后在main函数中调用usage函数,提示用户可以输入的命令。
部分main函数代码如下:
int main(int argc, char *argv[]) { int opt=0; int options_index=0; char *tmp=NULL; if(argc==1) { usage(); return 2; } while((opt=getopt_long(argc,argv,"912Vfrt:p:c:?h",long_options,&options_index))!=EOF ) { switch(opt) { case 0 : break; case 'f': force=1;break; case 'r': force_reload=1;break; case '9': http10=0;break; case '1': http10=1;break; case '2': http10=2;break; case 'V': printf(PROGRAM_VERSION"\n");exit(0); case 't': benchtime=atoi(optarg);break; case 'p': /* proxy server parsing server:port */ tmp=strrchr(optarg,':'); proxyhost=optarg; if(tmp==NULL) { break; } if(tmp==optarg) { fprintf(stderr,"Error in option --proxy %s: Missing hostname.\n",optarg); return 2; } if(tmp==optarg+strlen(optarg)-1) { fprintf(stderr,"Error in option --proxy %s Port number is missing.\n",optarg); return 2; } *tmp='\0'; proxyport=atoi(tmp+1);break; case ':': case 'h': case '?': usage();return 2;break; case 'c': clients=atoi(optarg);break; } }
当程序运行的时候,argc==1,接着执行usage函数,显示提示信息,
Comments