转载——getopt函数的使用

本文详细介绍了Linux下getopt及getopt_long函数的使用方法。getopt用于解析命令行参数中的短选项,getopt_long则支持长选项。文章通过实例展示了如何定义选项、处理选项参数等。

 

转载——getopt函数的使用
2007-12-16 12:15

 

作者写得很好。

每一天你都在使用大量的命令行程序,是不是感觉那些命令行参数用起来比较方便,他们都是使用getopt来实现的。
在Linux下使用getopt写程序是一种比较cool的事情,下面来简单的介绍一下getopt的使用。

=== getopt使用 ===

在讨论参数处理之前,我们先明确两个概念:选项、选项参数
gcc -g -o test test.c
我们经常使用上面的命令来编译程序,这里g和o就是选项,其中test就是o的选项参数

下面我们来看一下getopt:

首先是函数声明:
#include <unistd.h>
extern char *optarg;
extern int optind;
extern int optopt;
extern int opterr;
extern int optreset;
int getopt(int argc, char * const *argv, const char *optstring);

直接看一个例子:
/* getopt.c */
#include <unistd.h>
#include <stdio.h>
int main(int argc, char * argv[])
{
    int aflag=0, bflag=0, cflag=0;
    int ch;
    while ((ch = getopt(argc, argv, "ab:c")) != -1)
    {
        printf("optind: %d\n", optind);
        switch (ch) {
        case 'a':
            printf("HAVE option: -a\n");
            aflag = 1;
            break;
        case 'b':
            printf("HAVE option: -b\n");
            bflag = 1;
            printf("The argument of -b is %s\n", optarg);
            break;
        case 'c':
            printf("HAVE option: -c");
            cflag = 1;
            break;
        case '?':
            printf("Unknown option: %c\n",(char)optopt);
            break;
        }
    }
}

通过上面的例子,大家应该可以照猫画虎,就可以在自己的程序中使用getopt函数了。

getopt()每调用一次返回一个选项。
argc 和 argv 很显然就是 main 函数的两个参数。
字符串 optstring 可以包含下列元素:单个字符,字符后面接一个冒号说明后面跟随一个选项参数,字符后面接两个冒号说明后面跟随一个可有可无的选项参数。例如,一个选项字符 "x" 表示选项 "-x" ,选项字符 "x:" 表示选项和其参数 "-x argument",选项字符 "x::" 表示选项 x 的参数是可选的(“::” 是 GNU 增加的,不一定在所有的UNIX 系统下都可以使用)。
getopt()的返回后,如果有选项参数的话 optarg 指向选项参数,并且变量 optind 包含下一个 argv 参数作为对 getopt() 下一次调用的索引。变量 optopt 保存最后一个由 getopt() 返回的已知的选项。
当参数列已经到结尾时getopt()函数返回-1,当遇到一个未知的选项时 getopt 返回'?'。参数列中选项的解释可能会被'--'取消,由于它引起 getopt()给参数处理发送结束信号并返回-1。

很多时候,我们不希望输出任何错误信息,或更希望输出自己定义的错误信息。可以采用以下两种方法来更改getopt()函数的出错信息输出行为:
在调用getopt()之前,将opterr设置为0,这样就可以在getopt()函数发现错误的时候强制它不输出任何消息。
如果optstring参数的第一个字符是冒号,那么getopt()函数就会保持沉默,并根据错误情况返回不同字符,如下:
“无效选项” ―― getopt()返回'?',并且optopt包含了无效选项字符(这是正常的行为)。
“缺少选项参数” ―― getopt()返回':',如果optstring的第一个字符不是冒号,那么getopt()返回'?',这会使得这种情况不能与无效选项的情况区分开。
例如optstring为:a:b::c,表示a带一个参数,b可选,c不带参数
如果输入d,“无效选项“,getopt返回'?'
如果输入的a忘记带参数,“缺少选项参数”,getopt应返':' ;如果不再optstring的第一个字符不是':'的话,那么将会把这个错当成"无效参数",从而getopt返回'?';从而无法区别错误类型

比如:
wangyao@fisherman:~/Desktop/Advanced Linux Programming/ALP-listings/Trainning$ ./getopt -a -d -b foo
optind: 2
HAVE option: -a
./getopt: invalid option -- d
optind: 3
Unknown option: d
optind: 5
HAVE option: -b
The argument of -b is foo
wangyao@fisherman:~/Desktop/Advanced Linux Programming/ALP-listings/Trainning$ ./getopt -a -- -c -b foo
optind: 2
HAVE option: -a

getopt 的源代码在下面,getopt 将只会解释到 -a。

变量opterr和optind都被初始化为1。如果想要略去命令行的前几个参数,可以在调用getopt()前将optind设成其他值。
如果不希望getopt()输出出错信息,将全域变量 opterr 设为 0 即可。

是不是使用比较简单啊!

=== getopt_long使用 ===

我敢说,几乎每个人在接触到一个新的命令的时候,第一件干的事情就是 cmd -h 或者是 cmd --help,-h我们都知道是使用getopt来实现的,那么--help是怎么实现的呢?那就是getopt_long了,他可以支持长参数

先看一个例子程序:
#include <stdio.h>
#include <getopt.h>

int do_name, do_gf_name;
char *l_opt_arg;

static const char *shortopts = "l:ng";
struct option longopts[] = {
{"name", no_argument, NULL, 'n'},
{"gf_name", no_argument, NULL, 'g'},
{"love", required_argument, NULL, 'l'},
{0, 0, 0, 0},
};

int main (int argc, char *argv[])
{
int c;

while ((c = getopt_long (argc, argv, shortopts, longopts, NULL)) != -1)
    {
      switch (c)
   {
   case 'n':
      printf ("My name is LYR.\n");
      break;
   case 'g':
      printf ("Her name is BX.\n");
      break;
   case 'l':
      l_opt_arg = optarg;
      printf ("Our love is %s!\n", l_opt_arg);
      break;
   }
    }
return 0;
}

代码中我们使用getopt_long来实现长选项的解析,其中我们使用了一个结构体struct options的数组,struct options longopt[].
struct options的定义如下:
struct option{
     const char *name;
     int has_arg;
     int *flag;
     int val;
};

对结构中的各元素解释如下:
    const char *name
    这是选项名,前面没有短横线。譬如"help"、"verbose"之类。

    int has_arg
    描述了选项是否有选项参数。如果有,是哪种类型的参数,此时,它的值一定是下表中的一个。
    符号常量    数值    含义
    no_argument    0    选项没有参数
    required_argument    1    选项需要参数
    optional_argument    2    选项参数可选

    int *flag
    如果这个指针为NULL,那么 getopt_long()返回该结构val字段中的数值。如果该指针不为NULL,getopt_long()会使得它所指向的变量中填入val字段中的数值,并且getopt_long()返回0。如果flag不是NULL,但未发现长选项,那么它所指向的变量的数值不变。

    int val
    这个值是发现了长选项时的返回值,或者flag不是NULL时载入*flag中的值。典型情况下,若flag不是NULL,那么val是个真/假值,譬如1或0;另一方面,如果flag是NULL,那么 val通常是字符常量,若长选项与短选项一致,那么该字符常量应该与optstring中出现的这个选项的参数相同。

    每个长选项在长选项表中都有一个单独条目,该条目里需要填入正确的数值。数组中最后的元素的值应该全是0。数组不需要排序,getopt_long()会进行线性搜索。但是,根据长名字来排序会使程序员读起来更容易。

下面,我们看一下程序中的这个结构:
struct option longopts[] = {
{"name", no_argument, NULL, 'n'},
{"gf_name", no_argument, NULL, 'g'},
{"love", required_argument, NULL, 'l'},
{0, 0, 0, 0},
};
结构说明了三个常选项,name、gf_name、love三个选项,其中love需要选项;它们分别对应的短选项是n、g、l。
注意:上面结构体数组中的结构体的第三个参数flag都为NULL.

程序运行结果:
wangyao@fisherman:~/Desktop/Advanced Linux Programming/ALP-listings/Trainning$ ./getopt_long --name
My name is LYR.
wangyao@fisherman:~/Desktop/Advanced Linux Programming/ALP-listings/Trainning$ ./getopt_long -n
My name is LYR.
wangyao@fisherman:~/Desktop/Advanced Linux Programming/ALP-listings/Trainning$ ./getopt_long -l me
Our love is me!
wangyao@fisherman:~/Desktop/Advanced Linux Programming/ALP-listings/Trainning$ ./getopt_long --love me
Our love is me!
wangyao@fisherman:~/Desktop/Advanced Linux Programming/ALP-listings/Trainning$


=== Reference ===
GNU提供的getopt()函数的特点
http://blog.youkuaiyun.com/realduke2000/archive/2007/10/05/1812126.aspx
使用 getopt() 进行命令行处理
http://www.ibm.com/developerworks/cn/aix/library/au-unix-getopt.html

=== Questions ===
1、如何在getopt中指定可选参数
2、能否实现使用一个选项带多个参数,例如 scanner -i 1.1.1.1 2.2.2.2 3.3.3.3


#include <unistd.h> #include <signal.h> #include "libubus.h" static struct ubus_context *ctx; static struct blob_buf b; enum { REQ_STATION_COUNT, REQ_AP_NAME, __REQ_MAX }; /* 接收的解析格式, client发送过来的调用是 blobmsg_add_u32(&b, "getcnt", ap_index); */ static const struct blobmsg_policy fun2_message_parse_policy[__REQ_MAX] = { [REQ_STATION_COUNT] = { .name = "getcnt", .type = BLOBMSG_TYPE_INT32 }, [REQ_STATION_COUNT] = { .name = "apname", .type = BLOBMSG_TYPE_STRING }, }; static int fun2_handler(struct ubus_context *ctx, struct ubus_object *obj, struct ubus_request_data *req, const char *method, struct blob_attr *msg) { struct blob_attr *tb[__REQ_MAX]; //模拟ap数组中station的个数 int ap_array[] = {-1, 11111,22222,33333}; int ap_index; blobmsg_parse(fun2_message_parse_policy, __REQ_MAX, tb, blob_data(msg), blob_len(msg)); if (!tb[REQ_STATION_COUNT]) return UBUS_STATUS_INVALID_ARGUMENT; ap_index = blobmsg_get_u32(tb[REQ_STATION_COUNT]); fprintf(stdout, "someone ask the ap[%d]' info \n", ap_index); if (ap_index > (sizeof(ap_array) / sizeof(int))) ap_index = 0; blob_buf_init(&b, 0); //返回客户端请求的station个数 blobmsg_add_u32(&b, "stacnt", ap_array[ap_index]); //发送 ubus_send_reply(ctx, req, b.head); return 0; } static const struct ubus_method test_methods[] = { UBUS_METHOD("fun2", fun2_handler, fun2_message_parse_policy), }; static struct ubus_object_type test_object_type = UBUS_OBJECT_TYPE("server_fun", test_methods); //定义一个ubus 对象。 //其他进程通过调用相应字符串给执行对应的回调函数 //注册成功后可以通过命令ubus list -v 查看到obj //' server_fun' @79a8beac // "fun2" : {"getcnt":"Iterger","apname":"String"} static struct ubus_object test_object = { .name = "server_fun", .type = &test_object_type, .methods = test_methods, .n_methods = ARRAY_SIZE(test_methods), }; static void server_main(void) { int ret; //向ubusd新增一个对象,之后test_object在ubusd中有一个id号,其他进程通过该id号来向test_object发送消息。 ret = ubus_add_object(ctx, &test_object); if (ret) fprintf(stderr, "Failed to add object: %s\n", ubus_strerror(ret)); uloop_run(); } int main(int argc, char **argv) { const char *ubus_socket = NULL; int ch; while ((ch = getopt(argc, argv, "cs:")) != -1) { switch (ch) { case 's': ubus_socket = optarg; break; default: break; } } argc -= optind; argv += optind; uloop_init(); signal(SIGPIPE, SIG_IGN); ctx = ubus_connect(ubus_socket); if (!ctx) { fprintf(stderr, "Failed to connect to ubus\n"); return -1; } ubus_add_uloop(ctx); server_main(); ubus_free(ctx); uloop_done(); return 0; } ———————————————— 版权声明:本文为优快云博主「东升」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.youkuaiyun.com/liangdsing/article/details/53694495
最新发布
09-28
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值