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

一、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 数组,最后一个元素为**\0**表示字符串的结束。

**printf(%s)**就是输出字符串。

并且一般使用argv指针来访问、处理argv[]数组的内容。

C语言中,数组就是一个指针加偏移量。

所以argv则是指向一个指针数组argv[]的指针,不用定义,直接可以用。

在argv[]数组中存放的的指针指向输入命令的各部分**(调用程序、选项、参数)**。

2. 举例

下面我们用一个实例来理解argc和argv

/*
* argc: 命令行参数的个数
* argv: 字符指针数组(指向各个命令行参数的字符指针所构成的数组)
*/
int main(int argc, char* argv[]) // 接收命令行参数
{printf("argc=%d\n",argc);for (int i = 0; i < argc; i++) {printf("argv[%d]: %s\n", 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 %c\n", 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 = %c\t\t", opt);printf("optarg = %s\t\t",optarg);printf("optind = %d\t\t",optind);printf("argv[optind] = %s\n",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, --helprequired_argument 表明长选项必须带参数,如:–prefix /root或 --prefix=/rootoptional_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 = %c\t\t", opt);printf("optarg = %s\t\t",optarg);printf("optind = %d\t\t",optind);printf("argv[optind] =%s\t\t", argv[optind]);printf("option_index = %d\n",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 help\n",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 %s\n",interface);break;case 'd':delay_up = atoi(optarg);printf("delay_up %d\n",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.1peng@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 helppeng@ubuntu:~/work/test$ ./param --version
peng 1.1peng@ubuntu:~/work/test$ ./param --iface eth3 --delay-up 15
interface eth3
delay_up 15
talk is cheap!
test this code!

快操练起来吧!!!

更多嵌入式linux资料,后台留言:资料

也可以加一口君好友

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/160096.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Raspberry Pi 5 新一代单板计算机:树莓派5代 (介绍、入门、解疑)

树莓派5代正式发布后&#xff0c;硬件和性能的全面升级让众多开发者们都想入手感受一波&#xff0c;外观上Raspberry Pi 5 与前代产品非常相似&#xff0c;不过&#xff0c;在保留信用卡大小的整体尺寸的同时&#xff0c;也更新了一些设计元素&#xff0c;以适应新芯片组的功能…

python实现调和反距离空间插值法AIDW

1 简介 AIDW 主要是针对 IDW 的缺点进行了改进&#xff0c;考虑了样本点与预测点的位置&#xff0c;即方向和距离&#xff0c;具体见下图&#xff1a; 2 改进 IDW 公式&#xff1a; 从IDW算法可看出&#xff0c;插值点的估算值仅与插值样本距插值点的远近相关&#xff0c;并未…

贝叶斯AB测试

AB测试是用来评估变更效果的有效方法&#xff0c;但很多时候会运行大量AB测试&#xff0c;如果能够在测试中复用之前测试的结果&#xff0c;将有效提升AB测试的效率和有效性。原文: Bayesian AB Testing[1] 随机实验&#xff0c;又称AB测试&#xff0c;是行业中评估因果效应的既…

【Mysql】[Err] 1293 - Incorrect table definition;

基本情况 SQL文件描述 /* Navicat MySQL Data TransferSource Server : cm4生产-200 Source Server Version : 50725 Source Host : 192.168.1.200:3306 Source Database : db_wmsTarget Server Type : MYSQL Target Server Version : 50725 File…

vxe编辑保存表格

业务需求&#xff1a; 1、需要点击编辑时&#xff0c;全部表格显示编辑框&#xff0c;点击保存&#xff0c;全部保存。 2、因为位置问题&#xff0c;产品经理把24小时分成了两行&#xff0c;开发就得分两个表格。列标题是写死的&#xff0c;文字偏移也是写死的&#xff0c;其他…

服务器主机安全的重要性及防护策略

在数字化时代&#xff0c;服务器主机安全是任何组织都必须高度重视的问题。无论是大型企业还是小型企业&#xff0c;无论是政府机构还是个人用户&#xff0c;都需要确保其服务器主机的安全&#xff0c;以防止数据泄露、网络攻击和系统瘫痪等严重后果。 一、服务器主机安全的重…

__int128类型movaps指令crash

结论 在使用__int128时&#xff0c;如果__int128类型的内存起始地址不是按16字节对齐的话&#xff0c;有些汇编指令会抛出SIGSEGV使程序crash。 malloc在64位系统中申请的内存地址&#xff0c;是按16字节对齐的&#xff0c;但一般使用时经常会申请一块内存自己切割使用&#…

static和extern

1.extern extern 是⽤来声明外部符号的&#xff0c;如果⼀个全局的符号在A⽂件中定义的&#xff0c;在B⽂件中想使⽤&#xff0c;就可以使⽤ extern 进⾏声明&#xff0c;然后使⽤。 即在一个源文件中想要使用另一个源文件&#xff0c;即可通过这个extern来声明使用。 2.st…

未来制造业的新引擎:工业机器人控制解决方案

制造业正经历着一场革命性的变革 在这个变革的浪潮中&#xff0c;工业机器人成为推动制造业高效生产的关键力量。然而&#xff0c;要发挥机器人的最大潜力&#xff0c;一个强大而智能的控制系统是必不可少的。在这个领域&#xff0c;新一代的工业机器人控制解决方案正崭露头角&…

学习MySQL先有全局观,细说其发展历程及特点

学习MySQL先有全局观&#xff0c;细说其发展历程及特点 一、枝繁叶茂的MySQL家族1. 发展历程2. 分支版本 二、特点分析1. 常用数据库2. 选型角度及场景 三、三大组成部分四、总结 相信很多同学在接触编程之初&#xff0c;就接触过数据库&#xff0c;而对于其中关系型数据库中的…

这样写postman实现参数化,阿里p8都直呼牛逼

什么时候会用到参数化 比如&#xff1a;一个模块要用多组不同数据进行测试 验证业务的正确性 Login模块&#xff1a;正确的用户名&#xff0c;密码 成功&#xff1b;错误的用户名&#xff0c;正确的密码 失败 postman实现参数化 在实际的接口测试中&#xff0c;部分参数…

你的关联申请已发起,请等待企业微信的管理员确认你的申请

微信支付对接时&#xff0c;需要申请AppID,具体在下面的位置&#xff1a; 关联AppID&#xff0c;发起申请时&#xff0c;会提示这么一句话&#xff1a; 此时需要登录企业微信网页版&#xff0c;使用注册人的企业微信扫码登录进去&#xff0c;然后按照下面的步骤操作即可。 点击…

iEnglish全国ETP大赛:教育游戏助力英语习得

“seesaw,abacus,sword,feather,frog,lion,mouse……”11月18日,经过3局的激烈较量,“以过客之名队”的胡玲、黄长翔、林家慷率先晋级“玩转英语,用iEnglish”第三届全国ETP大赛的16强,在过去的周末中,还有TIK徘徊者队、不负昭华队、温柔杀戮者队先后晋级。据悉,根据活动规则,在…

电脑内存升级

ddr代兼容 自从DDR内存时代开启之后&#xff0c;只要满足内存的插槽规格相同(DDR3或DDR4或DDR5即为内存规格)这一条件&#xff0c;不同品牌、不同频率以及不同容量的茶品都可以一起使用&#xff0c;除了品牌和容量的影响之外&#xff0c;不同频率的搭配可能会造成性能方面的影…

面试官:什么是三色标记

程序员的公众号&#xff1a;源1024&#xff0c;获取更多资料&#xff0c;无加密无套路&#xff01; 最近整理了一波电子书籍资料&#xff0c;包含《Effective Java中文版 第2版》《深入JAVA虚拟机》&#xff0c;《重构改善既有代码设计》&#xff0c;《MySQL高性能-第3版》&…

HCIA-实验命令基础学习:

视频学习&#xff1a; 第一部分&#xff1a;基础学习。 19——子网掩码。 27——防火墙配置&#xff1a; 32——企业级路由器配置&#xff1a; 基础实验完成&#xff1a;&#xff08;完成以下目录对应的实验&#xff0c;第一部分基础实验就完成。&#xff09; 方法&#xff…

ILI9225 TFT显示屏16位并口方式驱动

所用屏及资料如后图&#xff1a; ILI9225&#xff0c;176*220&#xff0c;8位或16位并口屏&#xff0c;IM0接GND&#xff0c;电源及背光接3.3v 主控&#xff1a;CH32V307驱动&#xff08;库文件和STM32基本一样&#xff09; 一、源码 ILI9225.c #include "ILI9225.h&quo…

【SpringCloud】认识微服务、服务拆分以及远程调用

SpringCloud 1.认识微服务 1.1单体架构 单体架构&#xff1a;将业务的所有功能集中在一个项目中开发&#xff0c;打成一个包部署 单体架构的优缺点&#xff1a; **优点&#xff1a;**架构简单&#xff0c;部署成本低 **缺点&#xff1a;**耦合度高&#xff08;维护困难&…

笔记59:序列到序列学习Seq2seq

本地笔记地址&#xff1a;D:\work_file\&#xff08;4&#xff09;DeepLearning_Learning\03_个人笔记\3.循环神经网络\第9章&#xff1a;动手学深度学习~现代循环神经网络 a a a a a a a a a a a a a a a