• 正文
  • 推荐器件
  • 相关推荐
申请入驻 产业图谱

Linux程序之可变参数&选项那些事!

2023/11/15
1160
加入交流群
扫码加入
获取工程师必备礼包
参与热点资讯讨论

一、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!

快操练起来吧!!!

推荐器件

更多器件
器件型号 数量 器件厂商 器件描述 数据手册 ECAD模型 风险等级 参考价格 更多信息
ABCU-5730RZ 1 Avago Technologies FIBER OPTIC TRANSCEIVER, 1250Mbps(Tx), 1250Mbps(Rx), PANEL MOUNT, RJ-45 CONNECTOR, ROHS COMPLIANT, METAL, PACKAGE-20
暂无数据 查看
AT28C64B-15JU 1 Atmel Corporation EEPROM, 8KX8, 150ns, Parallel, CMOS, PQCC32, GREEN, PLASTIC, MS-016AE, LCC-32

ECAD模型

下载ECAD模型
$4.45 查看
C30617BFCH 1 PerkinElmer Inc Fiber Optic Device
暂无数据 查看

相关推荐

登录即可解锁
  • 海量技术文章
  • 设计资源下载
  • 产业链客户资源
  • 写文章/发需求
立即登录

公众号『一口Linux』号主彭老师,拥有15年嵌入式开发经验和培训经验。曾任职ZTE,某研究所,华清远见教学总监。拥有多篇网络协议相关专利和软件著作。精通计算机网络、Linux系统编程、ARM、Linux驱动、龙芯、物联网。原创内容基本从实际项目出发,保持原理+实践风格,适合Linux驱动新手入门和技术进阶。