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.rhkb.cn/news/201576.html

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

相关文章

C++之常用的排序算法

C之常用的排序算法 sort #include<iostream> using namespace std; #include<vector> #include<algorithm> #include<functional> void Myptint(int val) {cout << val << " "; }void test() {vector<int> v;v.push_back(…

一文搞懂什么是 GNU/Linux 操作系统

Author&#xff1a;rab 目录 前言一、UNIX二、Linux三、GNU 前言 你是否经常看见或听说过这么一句话&#xff1a;这是一个类 Unix 的 GNU/Linux 操作系统&#xff0c;你是怎么理解这句话的呢&#xff1f;想要搞懂这句话的含义&#xff0c;你需要了解以下三点基本常识。 一、U…

安装向量数据库milvus及其Attu

前置条件安装docker compose 在宿主机上创建文件目录 mkdir -p /home/sunyuhua/milvus/db mkdir -p /home/sunyuhua/milvus/conf mkdir -p /home/sunyuhua/milvus/etcd下载docker-compose.yml wget https://github.com/milvus-io/milvus/releases/download/v2.2.11/milvus-s…

ChatGPT暂时停止开通plus,可能迎来封号高峰期

前言: 前两日,chat gpt的创始人 San Altman在网上发表了,由于注册的使用量超过了他们的承受能力,为了确保每个人的良好使用体验,chat gpt将暂时停止开通gpt plus。 情况: 前段时间好像出现了官网崩溃的情况,就连api key都受到了影响,所以现在就开始了暂时停止plus的注…

源码安装Apache

一、下载Apache,源码安装Apache #下载 [rootlocalhost opt]# wget -c https://mirrors.aliyun.com/apache/httpd/httpd-2.4.58.tar.gz [rootlocalhost opt]# ls httpd-2.4.58.tar.gz [rootlocalhost opt]# tar -xf httpd-2.4.58.tar.gz [rootlocalhost opt]# ls httpd-2.4.58…

MATLAB | 官方举办的动图绘制大赛 | 第二周赛情回顾

今天带来一下MATHWORKS官方举办的迷你黑客大赛第三期(MATLAB Flipbook Mini Hack)的最新进展&#xff01;&#xff01;目前比赛已经进行了两周非常荣幸能够成为第一周的阶段性获奖者&#xff1a; 本来并不打算每周进行一次赛况讲解&#xff0c;但是由于字符限制改成了2000&…

px4+vio实现无人机室内定位

文章主要讲述px4 如何利用vins_fusion里程计数据实现在室内定位功能。 文章基于以下软、硬件展开。 硬件软件机载电脑&#xff1a; Intel NUC系统&#xff1a;Ubuntu 20.04相机&#xff1a; Intel Realsense D435iros&#xff1a;noetic飞控&#xff1a;Pixhawk 2.4.8固件&am…

HarmonyOS ArkTS 基础组件的使用(四)

1 组件介绍 组件&#xff08;Component&#xff09;是界面搭建与显示的最小单位&#xff0c;HarmonyOS ArkUI声明式开发范式为开发者提供了丰富多样的UI组件&#xff0c;我们可以使用这些组件轻松的编写出更加丰富、漂亮的界面。 组件根据功能可以分为以下五大类&#xff1a;…

浅谈Python装饰器原理与用法分析

前言 本文实例讲述了Python装饰器原理与用法。分享给大家供大家参考&#xff0c;具体如下&#xff1a; 1、装饰器的本质是函数&#xff0c;主要用来装饰其他函数&#xff0c;也就是为其他函数添加附加功能 2、装饰器的原则: (1) 装饰器不能修改被装饰的函数的源代码 (2) 装…

PyTorch微调终极指南1:预训练模型调整

如今&#xff0c;在训练深度学习模型时&#xff0c;通过根据自己的数据微调预训练模型来进行迁移学习&#xff08;transfer learning&#xff09;已成为首选方法。 通过微调这些模型&#xff0c;我们可以利用他们的专业知识并使它们适应我们的特定任务&#xff0c;从而节省宝贵…

【python基础(三)】操作列表:for循环、正确缩进、切片的使用、元组

文章目录 一. 遍历整个列表1. 在for循环中执行更多操作2. 在for循环结束后执行一些操作 二. 避免缩进错误三. 创建数值列表1. 使用函数range()2. 使用range()创建数字列表3. 指定步长。4. 对数字列表执行简单的统计计算5. 列表解析 五. 使用列表的一部分-切片1. 切片2. 遍历切片…

10、信息打点——APP小程序篇抓包封包XP框架反编译资产提取

APP信息搜集思路 外在——抓包封包——资产安全测试 抓包&#xff08;Fiddle&茶杯&burp&#xff09;封包&#xff08;封包监听工具&#xff09;&#xff0c;提取资源信息 资产收集——资源提取——ICO、MAD、hash——FOFA等网络测绘进行资产搜集 外在——功能逻辑 内在…

【原创】为MybatisPlus增加一个逻辑删除插件,让XML中的SQL也能自动增加逻辑删除功能

前言 看到这个标题有人就要说了&#xff0c;D哥啊&#xff0c;MybatisPlus不是本来就有逻辑删除的配置吗&#xff0c;比如TableLogic注解&#xff0c;配置文件里也能添加如下配置设置逻辑删除。 mybatis-plus:mapper-locations: classpath*:mapper/*.xmlconfiguration:mapUnd…

【Linux】-进程间通信-共享内存(SystemV),详解接口函数以及原理(使用管道处理同步互斥机制)

&#x1f496;作者&#xff1a;小树苗渴望变成参天大树&#x1f388; &#x1f389;作者宣言&#xff1a;认真写好每一篇博客&#x1f4a4; &#x1f38a;作者gitee:gitee✨ &#x1f49e;作者专栏&#xff1a;C语言,数据结构初阶,Linux,C 动态规划算法&#x1f384; 如 果 你 …

Vue3+Vite实现工程化,插值表达式和v-text以及v-html

1、插值表达式 插值表达式最基本的数据绑定形式是文本插值&#xff0c;它使用的是"Mustache"语法&#xff0c;即 双大括号{{}} 插值表达式是将数据 渲染 到元素的指定位置的手段之一插值表达式 不绝对依赖标签&#xff0c;其位置相对自由插值表达式中支持javascript的…

XmlElement注解在Java的数组属性上,以产生多个相同的XML元素

例如&#xff0c;下面这段XML数据&#xff0c;有多个data元素&#xff0c;并且它们级别相同: <?xml version"1.0" encoding"UTF-8"?><request><reqtype>05</reqtype><secret>test</secret><body><userid&…

93.STL-系统内置仿函数

目录 算术仿函数 关系仿函数 逻辑仿函数 C 标准库中提供了一些内置的函数对象&#xff0c;也称为仿函数&#xff0c;它们通常位于 <functional> 头文件中。以下是一些常见的系统内置仿函数&#xff1a; 算术仿函数 功能描述&#xff1a; 实现四则运算其中negate是一元…

个人博客项目 - 测试报告

文章目录 一、项目背景二、测试报告功能测试1.编写测试用例2.登录测试3.编写文章测试4.查看文章测试5.删除文章测试7.注销登录测试 自动化测试性能测试1.VUG2.进行场景设计3.生成性能测试报告 总结 本文开始 一、项目背景 通过学习测试相关的知识&#xff0c;动手实践并测试一…

Linux文件

目录 一、基本概念 二、研究进程和被打开文件的关系 &#xff08;一&#xff09;w方式 &#xff08;二&#xff09;a方式 三、认识系统接口&#xff0c;操作文件 &#xff08;一&#xff09;认识文件描述符 &#xff08;二&#xff09;举例 &#xff08;三&#xff09;…

ML-Net:通过深度学习彻底改变多标签分类

一、说明 多标签分类是一项具有挑战性的机器学习任务&#xff0c;其中输入可以同时属于多个类。传统的多标签分类方法通常依赖于将问题转化为一系列二元分类任务或使用集成方法。然而&#xff0c;深度学习的出现开创了多标签分类的新时代&#xff0c;ML-Net 等模型突破了该领域…