getopt函数和getopt_long函数

news2024/11/26 21:42:20

这个函数有点像无限迷宫,正确的路和错误的路都有很多,我们只需要能够满足当前需求就可以了,完全没有必要去探索每一条路。虽然,我很久以前试图这样干过。过滤后的回忆,只剩感觉了,过滤的多了,感觉都被冲散了。

getopt函数

Linux提供了getopt函数,它支持需要关联值和不需要关联值的选项,而且简单易用。

#include <unistd.h>

int getopt(int argc, char * const argv[],const char *optstring);
extern char *optarg;
extern int optind, opterr, optopt;

 getopt函数将传递给程序的main函数的argc和argv作为参数,同时接受一个选项指定符字符串optstring,该字符串告诉getopt哪些选项可用,以及它们是否有关联值。optstring只是一个字符列表,每个字符代表一个单字符选项。如果一个字符后面紧跟一个冒号(:),则表明该选项有一个关联值作为下一个参数。bash中的getopts命令执行类似的功能。

例如,我们可以用下面的调用来处理上面的例子:

getopt(argc,argv,"if:lr");

它允许几个简单的选项:-i、-l、-r和-f,其中-f选项后要紧跟一个文件名参数。使用相同的参数,但以不同的顺序来调用命令将改变程序的行为。你可以在本章的下一个实验部分进行尝试。

getopt的返回值是argv数组中的下一个选项字符(如果有的话)。循环调用getopt就可以依次得到每个选项。getopt有如下行为。

❑ 如果选项有一个关联值,则外部变量optarg指向这个值。

❑ 如果选项处理完毕,getopt返回-1,特殊参数--将使getopt停止扫描选项。

❑ 如果遇到一个无法识别的选项,getopt返回一个问号(? ),并把它保存到外部变量optopt中。

❑ 如果一个选项要求有一个关联值(例如例子中的-f),但用户并未提供这个值,getopt通常将返回一个问号(? )。如果我们将选项字符串的第一个字符设置为冒号(:),那么getopt将在用户未提供值的情况下返回冒号(:)而不是问号(? )。

外部变量optind被设置为下一个待处理参数的索引。getopt利用它来记录自己的进度。程序很少需要对这个变量进行设置。当所有选项参数都处理完毕后,optind将指向argv数组尾部可以找到其余参数的位置。

有些版本的getopt会在第一个非选项参数处停下来,返回-1并设置optind的值。而其他一些版本,如Linux提供的版本,能够处理出现在程序参数中任意位置的选项。注意,在这种情况下,getopt实际上重写了argv数组,把所有非选项参数都集中在一起,从argv[optind]位置开始。对GNU版本的getopt而言,这一行为是由环境变量POSIXLY_CORRECT控制的,如果它被设置,getopt就会在第一个非选项参数处停下来。此外,还有些getopt版本会在遇到未知选项时打印出错信息。注意,根据POSIX规范的规定,如果opterr变量是非零值,getopt就会向stderr打印一条出错信息。

实验getopt函数

在这个实验中,你将在程序中使用getopt函数,并将新程序命名为argopt.c:

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#define DEBUG_INFO(format, ...) printf("%s - %d - %s :: "format"\n",__FILE__,__LINE__,__func__ ,##__VA_ARGS__)

int main(int argc,char **argv)
{
    int opt;
    while((opt = getopt(argc,argv,"if:lr")) != -1){
        switch (opt)
        {
        case 'i':
        case 'l':
        case 'r':
            DEBUG_INFO("option:%c",opt);
            break;
        case 'f':
            DEBUG_INFO("option:%c filename = %s",opt,optarg);
            break;
        case ':':
            DEBUG_INFO("option:%c needs a value",opt);       
            break;
        case '?':
            DEBUG_INFO("unkown option:%c needs a value",optopt);       
            break;
        default:
            break;
        }
    }
    DEBUG_INFO("optind = %d ",optind);
    for(;optind < argc;optind++){
        DEBUG_INFO("argument:%s",argv[optind]);
    }
    return 0;
}

测试结果:

实验解析

这个程序循环调用getopt对选项参数进行处理,直到处理完毕,此时getopt返回-1。每个选项(包括未知选项和缺少关联值的选项)都有相应的处理动作。根据使用的getopt版本,你看到的输出可能和上面显示的略有不同,尤其是出错信息部分,但含义都是明确的。当所有选项都处理完毕后,程序像以前一样把其余参数都打印出来,但这次是从optind位置开始。

大部分情况下,我们不必考虑这么复杂的问题的。如下例所示:

通过参数配置IP地址和端口号

想要达到的效果如下所示:

argopt -i 192.168.0.5 -p 1502

-i选项:参数是IP地址

-p选项:参数是端口号

测试代码如下所示

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#define DEBUG_INFO(format, ...) printf("%s - %d - %s :: "format"\n",__FILE__,__LINE__,__func__ ,##__VA_ARGS__)

#define DEFAULT_IP "0.0.0.0"
#define DEFAULT_PORT ((uint16_t)502)
char ip[20];
uint16_t port;

int main(int argc,char **argv)
{
    int opt;
    while((opt = getopt(argc,argv,"i:p:")) != -1){
        switch (opt)
        {
        case 'i':
            DEBUG_INFO("option:%c ip = %s",opt,optarg);
            break;
        case 'p':
            DEBUG_INFO("option:%c port = %s",opt,optarg);     
            break;
        case '?':
            DEBUG_INFO("unkown option:%c needs a value",optopt);       
            break;
        default:
            DEBUG_INFO("unkown option:%c needs a value",optopt);
            break;
        }
    }
    DEBUG_INFO("optind = %d ",optind);
    for(;optind < argc;optind++){
        DEBUG_INFO("argument:%s",argv[optind]);
    }
    return 0;
}

执行性结果:

$ _build_/argopt -i 192.168.0.5 -p 1502
/big/work/ipc/argopt.c - 19 - main :: option:i ip = 192.168.0.5
/big/work/ipc/argopt.c - 22 - main :: option:p port = 1502
/big/work/ipc/argopt.c - 32 - main :: optind = 5

现在IP地址和端口的字符串已经获取到了,现在就是判断这两个参数是否合法了。

判断IP地址合法性,如下,(啥?有BUG?我的四十米大刀呢?)

int is_valid_ip(const char *ipstr){
    int res = 0;
    int ip[4];
    DEBUG_INFO("ipstr =%s",ipstr);

    sscanf(ipstr, "%d.%d.%d.%d", &ip[0],&ip[1],&ip[2],&ip[3]);
    for(int i = 0; i < 4; i++){
        printf("%d,",ip[i]);
        if(ip[i] > 255 || ip[i] < 0){
            res = -1;
        }
    }
    printf("\n");
    return res;
}

使用atoi函数判断端口号,首先知道atoi能解析什么样的字符串,并且解析到什么程度,如下代码:

#define DEBUG_IF(format, ...) printf(""format"\n" ,##__VA_ARGS__)
#define DEBUG_IF(format, ...) printf(""format"\n" ,##__VA_ARGS__)
void test_02(){
    int a;
    a  = atoi("1234");
    DEBUG_IF("a = %d",a);
    a  = atoi("a1234");
    DEBUG_IF("a = %d",a);
    a  = atoi("1234a");
    DEBUG_IF("a = %d",a);
    a  = atoi("12a34");
    DEBUG_IF("a = %d",a);
    
    a  = atoi("+1234");
    DEBUG_IF("a = %d",a);
    a  = atoi("-1234");
    DEBUG_IF("a = %d",a);
}

输出结果:

a = 1234
a = 0
a = 1234
a = 12

实验解析

情况1:“1234”合法,返回值是1234

情况2:"a1234",第一个字符是非法字符串,结果返回0

情况3:"1234a",最后一个字符是非法字符串,结果返回1234

情况4:"12a34",中间出现一个非数字字符,返回非数字字符前面的数字。

总之呢,使用之前先测下吧。

情况5和6:能够识别正好“+”和负号“-”。

整理后的代码如下所示:

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>

#define DEBUG_INFO(format, ...) printf("%s - %d - %s :: "format"\n",__FILE__,__LINE__,__func__ ,##__VA_ARGS__)

#define DEFAULT_IP "0.0.0.0"
#define DEFAULT_PORT ((uint16_t)502)
char gip[20];
uint16_t gport;

int is_valid_ip(const char *ipstr){
    int res = 0;
    int ip[4];
    DEBUG_INFO("ipstr =%s",ipstr);

    sscanf(ipstr, "%d.%d.%d.%d", &ip[0],&ip[1],&ip[2],&ip[3]);
    for(int i = 0; i < 4; i++){
        printf("%d,",ip[i]);
        if(ip[i] > 255 || ip[i] < 0){
            res = -1;
        }
    }
    printf("\n");
    return res;
}
int is_valid_port(const char *portstr){
    int port = atoi(portstr);
    if(port < 0 || port > 65535){
        return -1;
    }
    return port;
}

void test_01(int argc,char **argv){
    int opt;
    int temp;
    while((opt = getopt(argc,argv,"i:p:")) != -1){
        switch (opt)
        {
        case 'i':
            DEBUG_INFO("option:%c ip = %s",opt,optarg);
            memset(gip,0,sizeof(gip));
            if(is_valid_ip(optarg) == 0){
                memcpy(gip,optarg,strlen(optarg));
            }else{
                memcpy(gip,DEFAULT_IP,strlen(DEFAULT_IP));
            }          
            break;
        case 'p':
            DEBUG_INFO("option:%c port = %s",opt,optarg);
            temp = is_valid_port(optarg);     
            if(temp < 0){
                gport = DEFAULT_PORT;
            }else{
                gport = temp;
            }
            break;
        case '?':
            DEBUG_INFO("unkown option:%c needs a parameter",optopt);       
            break;
        default:
            DEBUG_INFO("unkown option:%c ",optopt);
            break;
        }
    }
    DEBUG_INFO("配置的IP地址:%s,和端口%d",gip,gport);

}
#define DEBUG_IF(format, ...) printf(""format"\n" ,##__VA_ARGS__)

int main(int argc,char **argv)
{
    test_01(argc,argv);
    // test_02();
    return 0;
}

测试效果:

$ ./argopt -i 192.168.5.110 -p 1502
/big/work/ipc/argopt.c - 44 - test_01 :: option:i ip = 192.168.5.110
/big/work/ipc/argopt.c - 17 - is_valid_ip :: ipstr =192.168.5.110
192,168,5,110,
/big/work/ipc/argopt.c - 53 - test_01 :: option:p port = 1502
/big/work/ipc/argopt.c - 69 - test_01 :: 配置的IP地址:192.168.5.110,和端口1502

如果是这样呢:./argopt -i .168.5.110 -p 1502,最后输出的结果就是下面这样的

/big/work/ipc/argopt.c - 69 - test_01 :: 配置的IP地址:0.0.0.0,和端口1502

getopt_long函数

许多Linux应用程序也接受比我们在前面例子中所用的单字符选项含义更明确的参数。GNU C函数库包含getopt的另一个版本,称作getopt_long,它接受以双划线(--)开始的长参数。

#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);

重写前面的代码:

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <getopt.h>

#define DEBUG_INFO(format, ...) printf("%s - %d - %s :: "format"\n",__FILE__,__LINE__,__func__ ,##__VA_ARGS__)

#define DEFAULT_IP "0.0.0.0"
#define DEFAULT_PORT ((uint16_t)502)
char gip[20];
uint16_t gport;

int is_valid_ip(const char *ipstr){
    int res = 0;
    int ip[4];
    DEBUG_INFO("ipstr =%s",ipstr);

    sscanf(ipstr, "%d.%d.%d.%d", &ip[0],&ip[1],&ip[2],&ip[3]);
    for(int i = 0; i < 4; i++){
        printf("%d,",ip[i]);
        if(ip[i] > 255 || ip[i] < 0){
            res = -1;
        }
    }
    printf("\n");
    return res;
}
int is_valid_port(const char *portstr){
    int port = atoi(portstr);
    if(port < 0 || port > 65535){
        return -1;
    }
    return port;
}

void test_01(int argc,char **argv){
    int opt;
    int temp;
    DEBUG_INFO("no_argument = %d",no_argument);
    DEBUG_INFO("required_argument = %d",required_argument);
    DEBUG_INFO("optional_argument = %d",optional_argument);

    struct option long_options[] ={
        {"ip",required_argument,NULL,'i'},
        {"port",required_argument,NULL,'p'},
        {0,0,0,0}
    };
    memcpy(gip,DEFAULT_IP,strlen(DEFAULT_IP));
    gport = DEFAULT_PORT;
    while((opt = getopt_long(argc,argv,"i:p:",long_options,NULL)) != -1){
        switch (opt)
        {
        case 'i':
            DEBUG_INFO("option:%c ip = %s",opt,optarg);
            memset(gip,0,sizeof(gip));
            if(is_valid_ip(optarg) == 0){
                memcpy(gip,optarg,strlen(optarg));
            }else{
                memcpy(gip,DEFAULT_IP,strlen(DEFAULT_IP));
            }          
            break;
        case 'p':
            DEBUG_INFO("option:%c port = %s",opt,optarg);
            temp = is_valid_port(optarg);     
            if(temp < 0){
                gport = DEFAULT_PORT;
            }else{
                gport = temp;
            }
            break;
        case '?':
            DEBUG_INFO("unkown option:%c needs a parameter",optopt);       
            break;
        default:
            DEBUG_INFO("unkown option:%c ",optopt);
            break;
        }
    }
    DEBUG_INFO("配置的IP地址:%s,和端口%d",gip,gport);

}
#define DEBUG_IF(format, ...) printf(""format"\n" ,##__VA_ARGS__)

int main(int argc,char **argv)
{
    test_01(argc,argv);
    // test_02();
    return 0;
}

测试结果:

$ ./argopt2 --ip 192.168.5.110 --port 1502
/big/work/ipc/argopt2.c - 41 - test_01 :: no_argument = 0
/big/work/ipc/argopt2.c - 42 - test_01 :: required_argument = 1
/big/work/ipc/argopt2.c - 43 - test_01 :: optional_argument = 2
/big/work/ipc/argopt2.c - 56 - test_01 :: option:i ip = 192.168.5.110
/big/work/ipc/argopt2.c - 18 - is_valid_ip :: ipstr =192.168.5.110
192,168,5,110,
/big/work/ipc/argopt2.c - 65 - test_01 :: option:p port = 1502
/big/work/ipc/argopt2.c - 81 - test_01 :: 配置的IP地址:192.168.5.110,和端口1502

实验解析

getopt_long函数比getopt多两个参数。第一个附加参数是一个结构数组,它描述了每个长选项并告诉getopt_long如何处理它们。第二个附加参数是一个变量指针,它可以作为optind的长选项版本使用。对于每个识别的长选项,它在长选项数组中的索引就写入该变量。在本例中,你不需要这一信息,因此第二个附加参数是NULL。

长选项数组由一些类型为struct option的结构组成,每个结构描述了一个长选项的行为。该数组必须以一个包含全0的结构结尾。

长选项结构在头文件getopt.h中定义,并且该头文件必须与常量_GNU_SOURCE一同包含进来,该常量启用getopt_long功能。我GNU编译器中,_GNU_SOURCE选项默认是已经定义的。

struct option {
    const char *name;
    int         has_arg;
    int        *flag;
    int         val;
};

 该结构的成员下如所示。

 要了解GNU对getopt扩展的其他选项及相关函数,请参考getopt的手册页。

小结 

是不是觉得有BUG,想想linux标准中,经常看到的类似一句话,未经定义的输入,会产生不会预知的输出。我只需要保证未经定义的输入不会让程序死掉就行了。这很难吗,不简单。

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

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

相关文章

阿里巴巴开源的Spring Cloud Alibaba手册在GitHub上火了

“微服务架构经验你有吗&#xff1f;” 前段时间一个朋友去面试&#xff0c;阿里面试官一句话问倒了他。实际上&#xff0c;不在BAT这样的大厂工作&#xff0c;是很难接触到支撑千亿级流量微服务架构项目的。但也正是这种难得&#xff0c;让各个大厂都抢着要这样的人才&#x…

高校如何拿下数据分类分级这道“题”? 建设方案与实践来了

数据安全若一场“大考”&#xff0c;数据分类分级绝对是道“必答题”。 对高校而言&#xff0c;同样如此。作为高层次人才培养与科学研究的重要基地&#xff0c;高校既拥有高价值的科研等敏感数据&#xff0c;又涉及大量师生个人信息&#xff0c;无论是开展数据战略还是数据安全…

35岁以上的测试人员有多少?

今天在某论坛上看到一个有意思的问题&#xff1a;35岁以上的测试人员有多少&#xff1f; 细细一琢磨&#xff0c;为什么这位朋友会有这样的疑问呢&#xff1f;根据提问者的年龄划分&#xff0c;有以下两种可能&#xff1a; 35岁以下的提问者&#xff1a;想了解下35岁是否真如…

第八章 Electron 实现音乐播放器之爬虫播放音乐

一、介绍 &#x1f680; ✈️ &#x1f681; &#x1f682; 我在第五章已经有一篇Electron爬虫的文章&#xff0c;主要写的爬取图片资源的案例。这篇开始讲解如何到一个音乐网站爬取音乐资源&#xff0c;并且进行在线播放&#xff0c;下载等等。 那么什么是爬虫呢。百度百科上…

今日小课堂:怎么翻译音频

想象一下&#xff0c;你正在与外国朋友聊天&#xff0c;但是你们之间有语言障碍。不用担心&#xff01;现在有许多翻译语音识别工具可以帮助你轻松应对这种情况。通过这些工具&#xff0c;你可以将语音转换为文字&#xff0c;然后再将其翻译成你所需的语言。接下来&#xff0c;…

会声会影2023中文版本V26.0.0.136

会声会影2023中文版是一款功能强大的视频编辑软件、大型视频制作软件、专业视频剪辑软件。会声会影专业视频编辑处理软件&#xff0c;可以用于剪辑合并视频&#xff0c;制作视频&#xff0c;屏幕录制&#xff0c;光盘制作&#xff0c;视频后期编辑、添加特效、字幕和配音等操作…

爬虫一定要用代理IP吗,不用行不行

目录 1、爬虫一定要用代理IP吗 2、爬虫为什么要用代理IP 3、爬虫怎么使用代理IP 4、爬虫使用代理IP的注意事项 1、爬虫一定要用代理IP吗 很多人觉得&#xff0c;爬虫一定要使用代理IP&#xff0c;否则将寸步难行。但事实上&#xff0c;很多小爬虫不需要使用代理IP照样工作…

【TA100】3.4 前向/延迟渲染管线介绍

一、渲染路径 1.什么是渲染路径&#xff08;Rendering Path&#xff09; ● 是决定光照实现的方式。&#xff08;也就是当前渲染目标使用的光照流程&#xff09; 二、渲染方式 首先看一下两者的直观的不同 前向/正向渲染-Forward Rendering 一句话概括&#xff1a;每个光…

openpose原理以及各种细节的介绍

前言&#xff1a; OpenPose是一个基于深度学习的人体姿势估计库&#xff0c;它可以从图像或视频中准确地检测和估计人体的关键点和姿势信息。OpenPose的目标是将人体姿势估计变成一个实时、多人、准确的任务。——本节介绍openpose的原理部分 把关键点按照定义好的规则从上到下…

Matter实战系列-----5.matter设备证书烧录

一、安装工具 1.1 安装Commander_Linux工具 下载地址 https://www.silabs.com/documents/public/software/SimplicityCommander-Linux.zip 下载完之后解压缩&#xff0c;在压缩包内执行命令如下 tar jxvf Commander_linux_x86_64_1v15p0b1306.tar.bz cd ./commander ./co…

启动appium服务的2种方法(python脚本cmd窗口)

目录 前言&#xff1a; 1. 通过cmd窗口命令启动 1.1 启动单个appium服务 1.2 启动多个appium服务 2. 通过python脚本来启动 2.1 启动单个appium服务 2.2 启动多个appium服务 3. 启动校验 3.1 通过cmd命令查看 3.1.1 查看指定端口号 3.1.2 查看全部端口号 3.2 通过生…

华为笔记本怎么用U盘重装Win10系统?

华为笔记本怎么用U盘重装Win10系统&#xff1f;华为笔记本拥有指纹识别、背光键盘、信号增强等功能&#xff0c;带给用户超棒的操作体验&#xff0c;用户现在想用U盘来重装华为笔记本Win10系统&#xff0c;但不知道具体怎么操作&#xff0c;这时候用户就可以按照以下分享的华为…

CMAC算法介绍

文章目录 一、简介二、符号三、步骤3.1 子秘钥生成3.2 计算MAC值 一、简介 CMAC&#xff08;Cipher Block Chaining-Message Authentication Code&#xff09;&#xff0c;也简称为CBC_MAC&#xff0c;它是一种基于对称秘钥分组加密算法的消息认证码。由于其是基于“对称秘钥分…

网络安全|渗透测试入门学习,从零基础入门到精通—渗透中的开发语言

目录 前面的话 开发语言 1、html 解析 2、JavaScript 用法 3、JAVA 特性 4、PHP 作用 PHP 能做什么&#xff1f; 5、C/C 使用 如何学习 前面的话 关于在渗透中需要学习的语言第一点个人认为就是可以打一下HTML&#xff0c;JS那些基础知识&#xff0c;磨刀不误砍柴…

RTU遥测终端机的应用场景有哪些?

遥测终端机又称智能RTU遥测终端机&#xff0c;是一种用于采集、传输和处理遥测数据的设备。在现代科技的发展中&#xff0c;遥测终端机扮演着重要的角色。它是一种能够实现远程监测和控制的关键设备&#xff0c;广泛应用于各个领域&#xff0c;包括水文水利、环境监测、工业自动…

Linux系统:优化命令sar

目录 一、理论 1.命令描述 2.命令作用 3.命令参数 4.实用实例 二、实验 1.压力测试 三、问题 1.Linux系统五大负载如何解决 2.为什么使用ab命令进行网络传输数据的压力测试 3.ab命令发送请求测试失败 四、总结 1.sar命令 2.ab命令 3.五大负载 一、理论 1.命令描…

MySQL的索引(我把梦想卖了 换成了柴米油盐)

文章目录 一、索引的概念二、索引的作用如何实现&#xff1f; 三、索引的副作用四、创建索引的原则依据创建索引的依据 五、索引的分类六、索引的增删改查1.创建索引&#xff08;1&#xff09;创建普通索引&#xff08;2&#xff09;创建唯一索引&#xff08;3&#xff09;创建…

Spring Boot进阶(51):Spring Boot项目如何集成 HTML?| 超级详细,建议收藏

1. 前言&#x1f525; 我们都知道&#xff0c;Spring Boot作为一款广泛应用于企业级的开发框架&#xff0c;其通过简化开发过程、提高开发效率赢得了众多开发者的青睐。在实际项目开发中&#xff0c;集成 HTML作为 Web 应用程序中的一个基本需求&#xff0c;也是现在极其常见的…

618最值得入手的数码产品有哪些?四款必入数码产品数码推荐

时间飞逝&#xff0c;不知不觉已经过了6月中旬&#xff0c;大家心心念念的618年中大促也即将迎来最后一波高潮。这次618大促各大品牌的优惠力度都非常可观&#xff0c;特别是数码产品类&#xff0c;可以说是今年最值得入手的时期。今天也为大家推荐几款高颜值数码好物&#xff…

在 Apple silicon Mac 上 DFU 模式修复或恢复 macOS 固件

搭载 Apple 芯片的 Mac 电脑 DFU 模式全新安装 macOS Ventura 请访问原文链接&#xff1a;https://sysin.org/blog/apple-silicon-mac-dfu/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页&#xff1a;sysin.org Mac computers with Apple silicon&a…