前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >如何优雅地处理命令行参数?

如何优雅地处理命令行参数?

作者头像
编程珠玑
发布于 2019-08-21 06:55:39
发布于 2019-08-21 06:55:39
1.1K00
代码可运行
举报
文章被收录于专栏:编程珠玑编程珠玑
运行总次数:0
代码可运行

前言

我们在Linux用到的命令常常支持很多参数,那么如何写一个程序,也像Linux命令一样支持很多参数呢?有什么什么优雅的处理方法?

命令行参数

在介绍如何处理命令行参数之前,简单介绍一下命令行参数,已经了解的朋友可以跳过此小节。 我们用一段代码,打印传给程序的每一个参数

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//来源:公众号【编程珠玑】
//博客:https://www.yanbinghu.com
//main.c
#include<stdio.h>
int main(int argc,char *argv[])
{
    int i = 0;
    for(i = 0;i < argc;i++)
    {
        printf("the %d para is %s\n",i,argv[i]);
    }
    return 0;
}

其中argc代表输入的参数个数,而argv中保存着参数具体的值,我们编译运行:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$ gcc -o main main.c
$ ./main 编程珠玑 C C++ Java 博客
the 0 para is ./main
the 1 para is 编程珠玑
the 2 para is C
the 3 para is C++
the 4 para is Java
the 5 para is 博客

我们依次打印了程序的输入参数,其中特别注意的是,第一个(下标为0)的参数是程序本身。

如何优雅地处理命令行参数

实际上我们通过getopt函数很容易实现。

函数声明

getopt就可以非常方便地处理简单参数了,其声明如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include<unistd.h>
extern int optind,opterr,optopt;
extern char *optarg;
int getopt(int argc,char *const argv[],const char *optstring);
参数介绍

几个参数说明如下:

  • argc 参数个数,可从main函数入口传入
  • argv 参数字符串数组,可从main函数入口传入
  • optstring 支持的选项字符串

第一个和第二个参数我们很熟悉,它和main函数的参数是一样的:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int main(int argc,char *argv[]);

第三个参数是什么意思呢?指的是你支持的选项,假设你的程序支持-h,-a,-n选项,并且-n选项后面要跟具体参数,那么optstring可以是:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
“han:

选项后面有一个冒号表示这个选项需要带参数。

它的返回值是int类型,如果出错,则返回-1,如果命令参数不识别,则返回’?‘。

外部变量

它有四个外部变量,含义分别如下:

  • optind 存放下一个要处理的字符串在argv数组中的下标,从1开始
  • opterr 如果选项发生错误,getopt会打印出错消息,如果设置为0,则不打印。
  • optopt 如果选项处理发生错误,它会指向导致出错的选项字符串
  • optarg 如果一个选项需要参数,如前面提到的n参数,由于后面有:,所以它需要参数,处理到它时,optarg会指向这个参数。
程序示例

我们仍然通过一个示例程序来看:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//来源:公众号【编程珠玑】
//博客:https://www.yanbinghu.com
//main1.c
#include<stdio.h>
#include<unistd.h>
extern int optind,opterr,optopt;
extern char *optarg;
int main(int argc,char *argv[])
{
    int c = 0; //用于接收选项
    /*循环处理参数*/
    while(EOF != (c = getopt(argc,argv,"han:")))
    {
        //打印处理的参数
        printf("start to process %d para\n",optind);
        switch(c)
        {
            case 'h':
                printf("we get option -h\n");
                break;
            case 'a':
                printf("we get option -a\n");
                break;
            //-n选项必须要参数
            case 'n':
                printf("we get option -n,para is %s\n",optarg);
                break;
            //表示选项不支持
            case '?':
                printf("unknow option:%c\n",optopt);
                break;
            default:
                break;
        }    
    }
    return 0;
}

编译运行:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$ gcc -o main1 main1.c

输入正常选项时,我们可以看到能正确获取到选项,获取到之后自然就可以做对应的动作了。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$ ./main1 -h -a
start to process 2 para
we get option -h
start to process 3 para
we get option -a

如果输入的选项不支持,就会提示未知选项:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$ ./main1 -u
./main1: invalid option -- 'u'
start to process 2 para
unknow option:u

对于-n选项,我们需要参数,如果没有参数会怎样?

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$ ./main1 -n
./main1: option requires an argument -- 'n'
start to process 2 para
unknow option:n

它会提示我们n选项需要参数,于是带上参数:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$ ./main1 -n
start to process 3 para
we get option -n,para is 2

怎么样?是不是很简单?

问题

但是不知道你有没有发现,上面的处理有个问题,那就是不支持长选项。

什么意思呢?

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$ ./main1 -ha
start to process 1 para
we get option -h
start to process 2 para
we get option -a

这种情况下,-ha被当成了两个选项,而不是一个选项,选项名为ha。

那么这种情况应该如何处理呢?就需要用到后面的函数啦。

来源:公众号【编程珠玑】 网站:https://www.yanbinghu.com

长选项处理

为了应对前面说的这种情况,需要用到下面两个函数中的一个:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#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);

它们的第一个第二个参数和getopt一样,第三个参数是一个struct option指针:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
           struct option {
               const char *name;
               int         has_arg;
               int        *flag;
               int         val;
           };

其成员含义分别如下:

  • name 长选项名称
  • has_arg 参数可选项,no_argument表示该选项后不带参,required_argument表示该选项后面带参数
  • *flag 匹配到选项后,如果flag是NULL,则返回val;如果不是NULL,则返回0,并且将val的值赋给flag指向的内存
  • val 匹配到选项后的返回值

longindex表示长选项在longopts中的索引值。

那getopt_long和getopt_long_only有什么区别呢? 实际上主要功能是差不多的,只是前者一个-时被解析成短选项,--被解析成长选项,而后者都被解析为长选项,举个例子,-help在前者被解析为h,e,l,p四个选项,而在后者是和--help一样的效果,即被认为是长选项。在getopt_long_only中,optstring可以为“”。

我们来看一个示例程序:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//来源:公众号【编程珠玑】
//博客:https://www.yanbinghu.com/2019/08/17/57486.html
//main2.c
#include<stdio.h>
#include<getopt.h>
extern int optind,opterr,optopt;
extern char *optargi;
//定义长选项
static struct option long_options[] = 
{
    {"help",no_argument,NULL,'h'},
    {"verbose",no_argument,NULL,'v'},
    {"number",required_argument,NULL,'n'}
};
int main(int argc,char *argv[])
{
    int index = 0;
    int c = 0; //用于接收选项
    /*循环处理参数*/
    while(EOF != (c = getopt_long(argc,argv,"hvn:",long_options,&index)))
    {
        switch(c)
        {
            case 'h':
                printf("we get option -h,index %d\n",index);
                break;
            case 'v':
                printf("we get option -v,index %d\n",index);
                break;
            //-n选项必须要参数
            case 'n':
                printf("we get option -n,para is %s\n",optarg);
                break;
            //表示选项不支持
            case '?':
                printf("unknow option:%c\n",optopt);
                break;
            default:
                break;
        }   
    }
    return 0;
}

编译运行:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$ gcc -o main2 main2.c
$ ./main2 --verbose --help 
we get option -v,index 1
we get option -h,index 0

注意,为什么-v参数的index是0?因为只有长选项才会对应index。

可以看到,使用--跟长选项,单个-后面跟短选项,但是如果是下面这样呢?

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$ ./main2 -help 
we get option -h,index 0
./main2: invalid option -- 'e'
unknow option:e
./main2: invalid option -- 'l'
unknow option:l
./main2: invalid option -- 'p'
unknow option:p

在这里,由于使用的getopt_long,它对于单个-的字符串,里面每个字符都当成了一个选项,因此help对它来说,其实是四个选项,但是后三个不被识别。如果想要-help也被当成长选项,那么就需要用到getopt_long_only函数了。

最后,再完整的用一遍:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$ ./main2 --help --verbose --number 10
we get option -h,index 0
we get option -v,index 1
we get option -n,para is 10

扩展说明

其实在处理选项的时候,如果参数前面有-,比如:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
rm -bar

这里的-bar会被当成一个选项,而不是文件名,因此想要把它当成文件名,而不是选项,需要采用下面这种方式:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
rm -- -bar

具体可以参考《linux中删除特殊名称文件的多种方式》。

总结

想要优雅地处理命令行参数,今天介绍的几个函数是有必要掌握了,那么是不是很想自己尝试一下呢?更多细节等你去发现。

推荐阅读:

“偷梁换柱”的库打桩机制

C语言入坑指南-数组之谜

认真理一理C++的构造函数

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-08-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 编程珠玑 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
getopt()、getopt_long()与getopt_long_only()获取命令行参数
众所周知,C/C++程序的主函数有两个参数。第一个参数是整型,可以获得包括程序名字的参数个数,第二个参数是字符数组指针或字符指针的指针,可以按顺序获得命令行上各个字符串参数。其原形是:
恋喵大鲤鱼
2018/08/03
4.4K0
Linux下解析命令行的标准形参(getopt)
在Linux下开发时,命令行的使用是必不可少的,经常会在命令行运行各种命令,启动服务,启动应用程序,查看函数用法等等;运行这些命令时都会传入一些参数,比如:
DS小龙哥
2022/01/10
8760
浅谈linux的命令行解析参数之getopt_long函数「建议收藏」
在linux中,经常需要各种命令,通常情况下都会带各种参数,而这些参数是如何解析的呢?通常使用GNU C提供的函数getopt、getopt_long、getopt_long_only函数来解析命令行参数。
全栈程序员站长
2022/08/11
1.7K0
浅谈linux的命令行解析参数之getopt_long函数「建议收藏」
ZooKeeper实践方案:(7) 分布式锁
分布式锁是控制分布式系统之间同步訪问共享资源的一种方式,须要相互排斥来防止彼此干扰来保证一致性。
全栈程序员站长
2022/07/05
1650
Shell中使用getopt、getopts命令
版权声明:本文为木偶人shaon原创文章,转载请注明原文地址,非常感谢。 https://blog.csdn.net/wh211212/article/details/53750366
shaonbean
2019/05/26
5.7K0
getopt用法说明
参考http://baike.baidu.com/item/getopt以及man-pages
全栈程序员站长
2022/08/11
5710
C/C++ 命令解析:getopt 方法详解和使用示例
getopt() 方法是用来分析命令行参数的,该方法由 Unix 标准库提供,包含在 <unistd.h> 头文件中。
全栈程序员站长
2022/08/11
2K0
C/C++ 命令解析:getopt 方法详解和使用示例
Web压力测试工具webbench
int method = METHOD_GET; //默认请求方法为GET方式
Criss@陈磊
2019/08/02
4.8K0
Web压力测试工具webbench
getopt解析
转载自http://www.cnitblog.com/zouzheng/archive/2007/04/02/25034.aspx
全栈程序员站长
2022/08/15
7040
深入理解getopt[通俗易懂]
【说明】 getopt 只是一个简单的解析命令可选项的函数,只能进行简单的格式命令解析,格式如下:
全栈程序员站长
2022/06/27
1.6K0
C语言中getopt()函数的用法[通俗易懂]
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/135394.html原文链接:https://javaforall.cn
全栈程序员站长
2022/08/18
3.1K0
C语言中getopt()函数的用法[通俗易懂]
getopt函数[通俗易懂]
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/131836.html原文链接:https://javaforall.cn
全栈程序员站长
2022/07/01
4210
你还不知道argc,argv是什么?
可以确定的是,argc是传递给应用程序的参数个数,argv是传递给应用程序的参数,且第一个参数为程序名。
DeROy
2021/11/08
1.3K0
命令行參数选项处理:getopt()及getopt_long()函数使用
在执行某个程序的时候,我们通常使用命令行參数来进行配置其行为。 命令行选项和參数控制 UNIX 程序,告知它们怎样动作。
全栈程序员站长
2022/07/07
5990
命令行參数选项处理:getopt()及getopt_long()函数使用
【C】解析命令行参数--getopt和getopt_long
在程序中一般都会用到命令行选项, 我们可以使用getopt 和getopt_long函数来解析命令行参数
零式的天空
2022/03/02
6880
来用C语言模拟一下ls命令
在linux下使用C语言,通过调用Linux系统的目录访问API来实现一个类似于ls命令功能的小程序,主要是可以练习程序对命令的解析和目录API函数的使用。
fensnote
2021/05/31
9880
webbench源码阅读
Webbench是一个在Linux下使用的非常简单的网站侧压工具。它使用fork()模拟多个客户端同时访问url,测试网站在压力下工作的性能。 只有socket.c和webbench.c两个文件.
yifei_
2022/11/14
3780
getopts(1) builtin command
在执行 Shell 脚本时,可以像运行应用程序一样传入相应的参数,在脚本内部根据传入的参数内容执行对应的操作。
恋喵大鲤鱼
2024/09/02
1540
这样处理shell脚本参数,爽多了!
只要顺序一变,参数就对应不上了。假设你有时候不需要第二个参数,要使用第三个参数,你是不是还必须得输入第二个参数?
编程珠玑
2020/05/18
21.6K1
Zookeeper实践方案:(4)命名服务
命名服务是指通过指定的名字来获取资源或者服务的地址,提供者的信息。利用Zookeeper非常easy创建一个全局的路径,而这个路径就能够作为一个名字。它能够指向集群中的集群。提供的服务的地址,远程对象等。简单来说使用Zookeeper做命名服务就是用路径作为名字,路径上的数据就是其名字指向的实体。
全栈程序员站长
2022/07/05
4200
相关推荐
getopt()、getopt_long()与getopt_long_only()获取命令行参数
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验