一、linux应用程序如何接收参数?
1. argc、argv
Linux应用程序执行时,我们往往通过命令行带入参数给程序,比如
ls?/dev/?-l??
其中参数 ?/dev/ 、**-l**都是作为参数传递给命令 ls
应用程序又是如何接收这些参数的?
通常应用程序都是从main函数开始执行,传统的main函数风格如下:
int?main(int?argc,?char*?argv[])?
argc:
程序的命令行参数的数量,用于统计参数数量。?
argv:
是一个指向一个字符串数组的指针,数组包含了参数,每个字符串就是一个参数,最后一个元素为0。
过一般习惯使用多级指针来操作字符串。
*char argv[]有时候我们也写成char argv,
**argv[]**是一个存放字符类型元素地址的数组。
因为 C 中是有字符串的概念的:将每个字符存放在 char 数组,最后一个元素为****表示字符串的结束。
**printf(%s)**就是输出字符串。
并且一般使用argv指针来访问、处理argv[]数组的内容。
C语言中,数组就是一个指针加偏移量。
所以argv则是指向一个指针数组argv[]的指针,不用定义,直接可以用。
在argv[]数组中存放的的指针指向输入命令的各部分(调用程序、选项、参数)。
2. 举例
下面我们用一个实例来理解argc和argv
/*
*?argc:?命令行参数的个数
*?argv:?字符指针数组(指向各个命令行参数的字符指针所构成的数组)
*/
int?main(int?argc,?char*?argv[])?//?接收命令行参数
{
?printf("argc=%dn",argc);
????for?(int?i?=?0;?i?<?argc;?i++)?{
????????printf("argv[%d]:?%sn",?i,?argv[i]);?//?遍历字符指针数组argv
????}
????return?0;
}
执行结果
peng@ubuntu:~/work$?./peng?arg1?arg2?arg3?
argc=4
argv[0]:?./peng
argv[1]:?arg1
argv[2]:?arg2
argv[3]:?arg3
参数与argc,argv关系如下:
二、选项
1. 选项含义
linux程序除了上述情况以外,我们还经常会遇到一个使用方法就是选项应用,
比如:ping命令
peng@ubuntu:~/work$?ping?-h
Usage:?ping?[-aAbBdDfhLnOqrRUvV]?[-c?count]?[-i?interval]?[-I?interface]
????????????[-m?mark]?[-M?pmtudisc_option]?[-l?preload]?[-p?pattern]?[-Q?tos]
????????????[-s?packetsize]?[-S?sndbuf]?[-t?ttl]?[-T?timestamp_option]
????????????[-w?deadline]?[-W?timeout]?[hop1?...]?destination
参数含义:
-a:尝试将IP地址解析为主机名。
-A:使用响应数据包中的附加数据。
-b:允许ping广播地址。
-B:不允许ping广播地址。
-c?count:设置要发送的数据包数量。
-d:使用SO_DEBUG选项。
-D:不将socket设为分离模式。
-f:向目标发送一个“强制”数据包。
-h:显示帮助信息。
-i?interval:设置发送数据包之间的时间间隔。
-I?interface:设置要使用的网络接口。
-l?preload:设置发送的数据包数量。
-m?mark:设置ping数据包的标记。
-M?pmtudisc_option:设置MTU发现选项。
-n:不要将IP地址解析为主机名。
-O:启用原始输出。
-p?pattern:设置数据包的模式。
-Q?tos:设置服务类型。
-r:不使用路由表,直接发送数据包到目标主机。
-R:启用记录路由。
-s?packetsize:设置数据包的大小。
-S?sndbuf:设置套接字的发送缓冲区大小。
-t?ttl:设置数据包的TTL值。
-T?timestamp_option:设置时间戳选项。
-U:使用UDP数据包。
-v:显示详细的ping命令输出。
-V:显示ping命令的版本信息。
-w?deadline:设置等待响应的时间。
-W?timeout:设置等待响应的超时时间。
destination:指定要ping的目标主机或IP地址。
这些 - 开头的都是选项,
[]表示可选的意思
[-aAbBdDfhLnOqrRUvV]????是无参的选项
[-c?count]?[-i?interval]?[-I?interface]
[-m?mark]?[-M?pmtudisc_option]?[-l?preload]?[-p?pattern]?[-Q?tos]
[-s?packetsize]?[-S?sndbuf]?[-t?ttl]?[-T?timestamp_option]
[-w?deadline]?[-W?timeout]?[hop1?...]??这些都是有参数的选项
destination???必须填写的参数
前辈们利用这点发明了“UNIX 风格”的命令,选项前面加一个横杠-,用于区分选项和参数。
2. 程序如何区分参数和选项?
在程序的代码实现中,按照 UNIX 的代码惯例,上来直接跳过第一个,然后判断指针指向的字符串第一个字符是不是-,如果是的,那么进入一个switch判断,用case列出多种支持的情况下,应该执行什么代码。
例如下面这样就可以判断选项和处理参数:
int?c;
while?(--argc?>?0?&&?(*++argv)[0]?==?'-'?{
?while?(c?=?*++argv[0]?{
??switch(c){
??case?'x':
???...
???break;
??case?'n':
???...
???break;
??default:
???printf("xxx:?illegal?opyion?%cn",?c);
???...
???break;
??}
?}
}
3. getopt、getopt_long
事实这么处理选项参数是比较麻烦的,
linux提供了选项解析的函数:
//?头文件
#include<unistd.h>
#include<getopt.h>??????????/*所在头文件?*/
int?getopt(intargc,?char?*?const?argv[],?const?char?*optstring);
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);
extern?char?*optarg;?????????/*系统声明的全局变量?*/
extern?int?optind,?opterr,?optopt;
三、getopt
1. 定义:
int?getopt(int?argc,?char?*?const?argv[],?const?char?*optstring);
功能:
?getopt是用来解析命令行选项参数的,但是只能解析短选项:?**-d?100**,不能解析长选项:**--prefix**
参数
?argc:
??main()函数传递过来的参数的个数
?argv:
??main()函数传递过来的参数的字符串指针数组
?optstring:
??选项字符串,告知?getopt()可以处理哪个选项以及哪个选项需要参数
返回:
?如果选项成功找到,返回选项字母;如果所有命令行选项都解析完毕,返回?-1;
?如果遇到选项字符不在?optstring?中,返回字符?‘?’;
?如果遇到丢失参数,那么返回值依赖于?optstring?中第一个字符,
?如果第一个字符是?‘:’?则返回’:‘,否则返回’?'并提示出错误信息。
2. optstring 含义 【重要】
下边重点举例说明optstring的格式意义:
char*optstring?=?“ab:c::”;
单个字符a?????????表示选项a没有参数????????????格式:-a即可,不加参数
单字符加冒号b:?????表示选项b有且必须加参数??????格式:-b?100或-b100,但-b=100错
单字符加2冒号c::???表示选项c可以有,也可以无?????格式:-c200,其它格式错误
上面这个 optstring 在传入之后,getopt 函数将依次检查命令行是否指定了 -a, -b, -c(这需要多次调用 getopt 函数,直到其返回-1),当检查到上面某一个参数被指定时,函数会返回被指定的参数名称(即该字母)
系统声明的4个全局变量含义如下:
optarg?——?指向当前选项参数(如果有)的指针。
optind?——?再次调用?getopt()?时的下一个?argv指针的索引。
optopt?——?最后一个未知选项。
opterr??——?如果不希望getopt()打印出错信息,则只要将全域变量opterr设为0即可。
3. 实例
说千道万,不如来一个实例:
#include<stdio.h>
#include<unistd.h>
#include<getopt.h>
int?main(intargc,?char?*argv[])
{
????int?opt;
????char?*string?=?"a::b:c:d";
????while?((opt?=?getopt(argc,?argv,?string))!=?-1)
????{??
????????printf("opt?=?%ctt",?opt);
????????printf("optarg?=?%stt",optarg);
????????printf("optind?=?%dtt",optind);
????????printf("argv[optind]?=?%sn",argv[optind]);
????}??
}
- 正确输入参数,执行结果如下:
 
peng@ubuntu:~/work/test$?./peng?-a100?-b?200?-c?300?-d
opt?=?a??optarg?=?100??optind?=?2??argv[optind]?=?-b
opt?=?b??optarg?=?200??optind?=?4??argv[optind]?=?-c
opt?=?c??optarg?=?300??optind?=?6??argv[optind]?=?-d
opt?=?d??optarg?=?(null)??optind?=?7??argv[optind]?=?(null)
或者
ork/test$?./peng?-a100?-b200?-c300?-d?
opt?=?a??optarg?=?100??optind?=?2??argv[optind]?=?-b200
opt?=?b??optarg?=?200??optind?=?3??argv[optind]?=?-c300
opt?=?c??optarg?=?300??optind?=?4??argv[optind]?=?-d
opt?=?d??optarg?=?(null)??optind?=?5??argv[optind]?=?(null)
- 输入选项参数错误的情况
 
peng@ubuntu:~/work/test$?./peng?-a?100?-b?200?-c?300?-d
opt?=?a??optarg?=?(null)??optind?=?2??argv[optind]?=?100
opt?=?b??optarg?=?200??optind?=?5??argv[optind]?=?-c
opt?=?c??optarg?=?300??optind?=?7??argv[optind]?=?-d
opt?=?d??optarg?=?(null)??optind?=?8??argv[optind]?=?(null)
导致解析错误,第一个 optarg = null,实际输入参数 100,由于格式不正确造成的(可选参数格式固定)
- 参数丢失,也会导致错误
 
peng@ubuntu:~/work/test$?./peng?-a?-b?200?-c?
opt?=?a??optarg?=?(null)??optind?=?2??argv[optind]?=?-b
opt?=?b??optarg?=?200??optind?=?4??argv[optind]?=?-c
./peng:?option?requires?an?argument?--?'c'
opt?=????optarg?=?(null)??optind?=?5??argv[optind]?=?(null)
c选项是必须有参数的
- 命令行选项未定义,-e选项未在optstring中定义,会报错:
 
peng@ubuntu:~/work/test$?./peng?-t
./peng:?invalid?option?--?'t'
opt?=????optarg?=?(null)??optind?=?2??argv[optind]?=?(null)
四、getopt_long
1. 定义:
int?getopt_long(int?argc,?char?*?const?argv[],?const?char?*optstring,
const?struct?option?*longopts,int?*longindex);
功能:
?包含?getopt?功能,增加了解析长选项的功能如:--prefix?--help
参数:
?longopts?
??指明了长参数的名称和属性
?longindex?
??如果longindex非空,它指向的变量将记录当前找到参数符合longopts里的第几个元素的描述,即是?longopts?的下标值
返回:
?对于短选项,返回值同?getopt?函数;
?对于长选项,
??如果?flag?是?NULL?,返回?val?,否则返回?0?;
?对于错误情况返回值同?getopt?函数
2. struct option
struct?option?{
?const?char??*name;???????/*?参数名称?*/
?int??????????has_arg;????/*?指明是否带有参数?*/
?int??????????*flag;??????/*?flag=NULL时,返回value;不为空时,*flag=val,返回0?*/
?int??????????val;????????/*?用于指定函数找到选项的返回值或flag非空时指定*flag的值?*/
};?
参数has_arg 说明:
has_arg ?指明是否带参数值,其数值可选:
?no_argument?
??表明长选项不带参数,如:–name,?--help
?required_argument?
??表明长选项必须带参数,如:–prefix?/root或?--prefix=/root
?optional_argument?
??表明长选项的参数是可选的,如:–help或?–prefix=/root,其它都是错误
3. 实例
#include<stdio.h>
#include<unistd.h>
#include<getopt.h>
int?main(intargc,?char?*argv[])
{
????int?opt;
????int?digit_optind?=?0;
????int?option_index?=?0;
????char?*string?=?"a::b:c:d";
????static?struct?option?long_options[]?=
????{??
????????{"reqarg",?required_argument,NULL,?'r'},
????????{"optarg",?optional_argument,NULL,?'o'},
????????{"noarg",??no_argument,?????????NULL,'n'},
????????{NULL,?????0,??????????????????????NULL,?0},
????};?
????while((opt?=getopt_long_only(argc,argv,string,long_options,&option_index))!=?-1)
????{??
????????printf("opt?=?%ctt",?opt);
????????printf("optarg?=?%stt",optarg);
????????printf("optind?=?%dtt",optind);
????????printf("argv[optind]?=%stt",?argv[optind]);
????????printf("option_index?=?%dn",option_index);
????}??
}
- 正确执行命令
 
peng@ubuntu:~/work/test$?./long?--reqarg?100?--optarg=200?--noarg
opt?=?r??optarg?=?100??optind?=?3??argv[optind]?=--optarg=200??option_index?=?0
opt?=?o??optarg?=?200??optind?=?4??argv[optind]?=--noarg??option_index?=?1
opt?=?n??optarg?=?(null)??optind?=?5??argv[optind]?=(null)??option_index?=?2
或者
peng@ubuntu:~/work/test$?./long?–reqarg=100?--optarg=200?--noarg
opt?=?o??optarg?=?200??optind?=?3??argv[optind]?=--noarg??option_index?=?1
opt?=?n??optarg?=?(null)??optind?=?4??argv[optind]?=(null)??option_index?=?2
- 可选选项可以不给参数
 
peng@ubuntu:~/work/test$?./long?--reqarg?100?--optarg?--noarg
opt?=?r??optarg?=?100??optind?=?3??argv[optind]?=--optarg??option_index?=?0
opt?=?o??optarg?=?(null)??optind?=?4??argv[optind]?=--noarg??option_index?=?1
opt?=?n??optarg?=?(null)??optind?=?5??argv[optind]?=(null)??option_index?=?2
- 输入长选项错误的情况
 
peng@ubuntu:~/work/test$?./long?--reqarg?100?--optarg?200?--noarg
opt?=?r??optarg?=?100??optind?=?3??argv[optind]?=--optarg??option_index?=?0
opt?=?o??optarg?=?(null)??optind?=?4??argv[optind]?=200??option_index?=?1
opt?=?n??optarg?=?(null)??optind?=?6??argv[optind]?=(null)??option_index?=?2
五、getopt_long_only
getopt_long_only 函数与 getopt_long 函数使用相同的参数表,在功能上基本一致
只是 getopt_long 只将 --name 当作长参数,但 getopt_long_only 会将 --name 和 -name 两种选项都当作长参数来匹配
getopt_long_only 如果选项 -name 不能在 longopts 中匹配,但能匹配一个短选项,它就会解析为短选项。
六、综合实例
下面这个例子,是一口君从开源项目ifplug提取出来的命令提取小例子,
大家可以根据自己需要,基于这个框架,定制自己的程序。
#define?_GNU_SOURCE
#include?<stdlib.h>
#include?<stdio.h>
#include?<string.h>
#include?<unistd.h>
#include?<errno.h>
#include?<getopt.h>
#include?<sys/param.h>
#define?ETHCHECKD_VERSION?"1.1"
int?delay_up?=?0;
char?*interface?=?"eth0";
void?usage(char?*p)?{
????if?(strrchr(p,?'/'))
????????p?=?strchr(p,?'/')+1;
????printf("%s?[options]n"
???????????"???-i?--iface=IFACE??????????Specify?ethernet?interface?(%s)n"?
???????????"???-d?--delay-up=SECS????????Specify?delay?time?(%i)n"
???????????"???-h?--help?????????????????Show?this?helpn",
???????????p,
???????????interface,
???????????delay_up);
}
void?parse_args(int?argc,?char?*argv[])?{
????static?struct?option?long_options[]?=?{
????????{"iface",????????????????required_argument,?0,?'i'},
????????{"delay-up",?????????????required_argument,?0,?'d'},
????????{"help",?????????????????no_argument,?0,?'h'},
????????{"version",??????????????no_argument,?0,?'v'},
????????{0,?0,?0,?0}
????};
????int?option_index?=?0;
????int?help?=?0,?_kill?=?0,?_check?=?0,?_version?=?0,?_suspend?=?0,?_resume?=?0,?_info?=?0;
????
????for?(;;)?{
????????int?c;
????????
????????if?((c?=?getopt_long(argc,?argv,?"i:d:hv",?long_options,?&option_index))?<?0)
????????????break;
????????switch?(c)?{
????????????case?'i'?:
????????????????interface?=?strdup(optarg);
????printf("interface?%sn",interface);
????????????????break;
????????????case?'d':
????????????????delay_up?=?atoi(optarg);
????printf("delay_up?%dn",delay_up);
????????????????break;
????????????case?'h':
????????????????usage(argv[0]);
????????????????break;
?????????????case?'v':
????????????????printf("peng?"ETHCHECKD_VERSION"n");
????????????????break;
????????????default:
????????????????fprintf(stderr,?"Unknown?parameter.n");
????????????????exit(1);
????????}
????}
????
}
static?volatile?int?alarmed?=?0;
int?main(int?argc,?char*?argv[])?{
????parse_args(argc,?argv);
????return?0;
}
下面是测试结果
- 短选项
 
peng@ubuntu:~/work/test$?./param?-h
param?[options]
???-i?--iface=IFACE??????????Specify?ethernet?interface?(eth0)
???-d?--delay-up=SECS????????Specify?delay?time?(0)
???-h?--help?????????????????Show?this?help
peng@ubuntu:~/work/test$?./param?-v
peng?1.1
peng@ubuntu:~/work/test$?./param?-vh
peng?1.1
param?[options]
???-i?--iface=IFACE??????????Specify?ethernet?interface?(eth0)
???-d?--delay-up=SECS????????Specify?delay?time?(0)
???-h?--help?????????????????Show?this?help??
???
peng@ubuntu:~/work/test$?./param?-i?eth3?-d?15
interface?eth3
delay_up?15?
peng@ubuntu:~/work/test$?./param?-i?eth3?-d?15?-h
interface?eth3
delay_up?15
param?[options]
???-i?--iface=IFACE??????????Specify?ethernet?interface?(eth3)
???-d?--delay-up=SECS????????Specify?delay?time?(15)
???-h?--help?????????????????Show?this?help
- 长选项
 
peng@ubuntu:~/work/test$?./param?--help
param?[options]
???-i?--iface=IFACE??????????Specify?ethernet?interface?(eth0)
???-d?--delay-up=SECS????????Specify?delay?time?(0)
???-h?--help?????????????????Show?this?help
???
peng@ubuntu:~/work/test$?./param?--version
peng?1.1
peng@ubuntu:~/work/test$?./param?--iface?eth3?--delay-up?15
interface?eth3
delay_up?15
talk?is?cheap!
test?this?code!
快操练起来吧!!!
								
								
								
1160
					
								
下载ECAD模型