getopt函数和getopt_long函数

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

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标准中,经常看到的类似一句话,未经定义的输入,会产生不会预知的输出。我只需要保证未经定义的输入不会让程序死掉就行了。这很难吗,不简单。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【简单便捷】解决Ubuntu内存不足问题:Ubuntu16.0.4 进行内存扩容

文章目录 电脑环境前言一、总述二、先在标题&#xff1a;虚拟机-->设置上进行扩容三、扩容之后 打开终端 执行 sudo apt install gparted四、执行 sudo gparted五、扩容成功六、重启测试 可以看到大概率成功了。 电脑环境 Windows 11 专业版系统 前言 在开发初期&#xf…

二叉树的相关操作

一.二叉树 本文的数据结构基于C语言练习。 C语言中的二叉树是一种数据结构&#xff0c;用于表示具有层次关系的数据集合。它由一个根节点开始&#xff0c;每个节点最多有两个子节点&#xff0c;分别称为左子节点和右子节点。 二叉树有许多相关性质&#xff0c;其中一些重要的包…

【CSS---定位基础篇】

CSS---定位基础篇 CSS-----基础定位:一 、 学习定位原因&#xff1a;&#xff08;定位的作用&#xff09;二 、定位组成&#xff1a;2.1 四种定位模式&#xff1a;&#xff08;1&#xff09;静态定位&#xff08;了解&#xff09;&#xff1a;&#xff08;2&#xff09;相对定位…

ElasticSearch笔记02-ElasticSearch入门

ElasticSearch安装 下载软件 ElasticSearch的官网&#xff0c;视频教程里用的Version是7.8.0&#xff0c;所以&#xff0c;我们也是用7.8.0版本的ElasticSearch。 下载地址&#xff1a;https://www.elastic.co/cn/downloads/past-releases#elasticsearch&#xff0c;然后搜索…

lua的元表与元方法理解

元表 在 Lua 中&#xff0c;元表&#xff08;metatable&#xff09;是一种特殊的表&#xff0c;用于定义另一个表的行为。每个表都有一个关联的元表&#xff0c;通过元表可以重载表的各种操作&#xff0c;例如索引、新索引、相加等。在 Lua 中&#xff0c;元表的使用非常灵活&…

二、线性神经网络

文章目录 前言一、线性回归1. 线性回归的基本元素1.1 线性模型1.2 损失函数1.3 解析解1.4 梯度下降1.5 用模型进行预测 2. 正态分布与平方损失3. 从线性回归到深度网络 二、线性回归的代码实现1. 生成数据集2. 读取数据集2.1 手动实现读取数据集2.2 简洁实现读取数据集 3. 初始…

基于SpringBoot+Vue的自习室预订系统设计与实现

博主介绍&#xff1a; 大家好&#xff0c;我是一名在Java圈混迹十余年的程序员&#xff0c;精通Java编程语言&#xff0c;同时也熟练掌握微信小程序、Python和Android等技术&#xff0c;能够为大家提供全方位的技术支持和交流。 我擅长在JavaWeb、SSH、SSM、SpringBoot等框架下…

深度学习卷积神经网络CNN之ResNet模型网络详解说明(超详细理论篇)

1.ResNet背景 2. ResNet论文 3. ResNet模型结构 4. ResNet优缺点 一、ResNet背景 ResNet 在2015 年由微软研究院提出的一种深度卷积神经网络结构&#xff0c;在ILSVRC&#xff08;ImageNet Large Scale Visual Recognition Challenge&#xff09;中取得了冠军&#xff08;分类…

OpenCV(图像处理)-基于Python-形态学处理-开运算、闭运算、顶帽、黑帽运算

1. 形态学2. 常用接口2.1 cvtColor()2.2 图像二值化threshod()自适应阈值二值化adaptiveThreshod() 2.3 腐蚀与膨胀erode()getStructuringElement()dilate() 2.4开、闭、梯度、顶帽、黑帽运算morphologyEx() 1. 形态学 OpenCV形态学是一种基于OpenCV库的数字图像处理技术&…

如何安装MySQL数据库

目录 什么是MySQL数据库 第一步 安装依赖环境 第二步 创建MySQL相关进程用户 第三步 导入MySQL相关包 第四步 解包到指定目录下 第五步 切换到MySQL目录下编译安装 第六步 编译 第七步 更改指定文件的所有者和所属组 第八步 进入指定配置文件清空内容 第九步 配置指定…

IDEA Build Artifacts 功能使用总结

文章目录 创建Artifact步骤Build Artifact步骤 打开IDEA&#xff0c;在没有创建Artifact时&#xff0c;菜单"Build -> Build Artifacts…“是灰色的&#xff0c;不可用状态。 所以&#xff0c;第一步是进入"File -> Project Structure…”&#xff0c;创建Arti…

12-代码实战——服务器版表白墙

目录 1.版本一&#xff1a;将数据存到内存中 ①约定前后端交互接口 a.添加表白信息&#xff1a; b.查询表白列表&#xff1a; ②在webapp包下创建message-wall.html前端文件 ③在java包下创建AddMessageServlet后端类 ④在java包下创建MessageListServlet后端类 2.版本…

【Azure】微软 Azure 基础解析(七)Azure 网络服务中的虚拟网络 VNet、网关、负载均衡器 Load Balancer

本系列博文还在更新中&#xff0c;收录在专栏&#xff1a;「Azure探秘&#xff1a;构建云计算世界」 专栏中。 本系列文章列表如下&#xff1a; 【Azure】微软 Azure 基础解析&#xff08;三&#xff09;描述云计算运营中的 CapEx 与 OpEx&#xff0c;如何区分 CapEx 与 OpEx…

通过 Python 封装关键词搜索阿里巴巴商品api接口

以下是使用 Python 封装关键词搜索阿里巴巴商品列表数据的步骤&#xff1a; 使用 requests 库向阿里巴巴搜索接口发送 HTTP 请求&#xff0c;可以使用 GET 或 POST 方法&#xff0c;请求参数中应包含搜索关键词、每页展示数量、当前页码等信息。 解析返回的 response 中的 HTM…

【Java高级语法】(六)内部类Inner Class:这可能是史上最全的关于内部类的学习资料~

Java高级语法详解之包装类 :one: 概念:two: 优缺点:three: 使用2.1 成员内部类2.2 局部内部类2.3 匿名内部类2.4 静态内部类2.5 小结&#xff1a;外部类访问四种内部类的特点2.6 小结&#xff1a;其他类访问四种内部类的特点 :four: 内部类与外部类的关系:five: 应用场景:six: …

01-抒写代码之诗:Golang 关键字的文学探索

&#x1f4c3;个人主页&#xff1a;个人主页 &#x1f525;系列专栏&#xff1a;Golang基础 &#x1f4ac;Go&#xff08;又称Golang&#xff09;是由Google开发的开源编程语言。它结合了静态类型的安全性和动态语言的灵活性&#xff0c;拥有高效的并发编程能力和简洁的语法。G…

Jenkins结合gitee自动化部署SpringBoot项目

安装 安装教程 插件选择 Gitee Plugin 配置 源码管理 填写源码地址 注意&#xff1a;请确保genkins所在的服务器有权限git拉取远程仓库代码&#xff0c;如果不可以请参考ssh配置centos 配置ssh拉取远程git代码 源码管理 构建触发器 1.勾选Gitee webhook 触发构建 2.生成we…

论文笔记:MEASURING DISENTANGLEMENT: A REVIEW OF METRICS

0 摘要 学习解缠和表示数据中的变化因素是人工智能中的一个重要问题。虽然已经取得了许多关于学习这些表示的进展&#xff0c;但如何量化解缠仍然不清楚。 虽然存在一些度量标准&#xff0c;但对它们的隐含假设、真正衡量的内容以及限制了解甚少。因此&#xff0c;当比较不同的…

一键开启GPT 平行时空模式

不知道大家日常使用GPT的时候&#xff0c;在一次会话中是如何完成同类任务的对话的? 简单点来说&#xff0c;假设你已经完成了角色设定&#xff0c;比如你设定GPT是一名文案编辑&#xff0c;那么接下来你会多次给它提交稿件让它进行编辑&#xff0c;那么在多次提交的时候&…

抽象工厂模式

一.定义 抽象工厂模式(Abstract Factory Pattern)是一种比较常用的模式&#xff0c;其定义如下: Provide an interface for creating families of related or dependent objects without specifyingtheir concrete classes.(为创建一组相关或相互依赖的对象提供一个接口&#…