您的位置:首页 > 运维架构

使用getopt_long()从命令行获取参数,struct option

2017-02-06 14:12 387 查看
本文来自:http://blog.csdn.net/yui/article/details/5669922

函数说明

#include
函数说明:
函数getopt用来解析命令行参数。
函数getopt_long支持长选项的命令行解析。

函数原型:
intgetopt_long(int argc, char* constargv[], 
                     const char*optstring,
                     const struct option*longopts, 
                     int*longindex);
参数:
argc、argv直接从main函数中获取。
opting是选项参数组成的字符串,由下列元素组成:

1.单个字符,表示选项,
2.单个字符后接一个冒号:表示该选项后必须跟一个参数。参数紧跟在选项后或者以空格隔开。该参数的指针赋给optarg。
3.单个字符后跟两个冒号,表示该选项后可以有参数也可以没有参数。如果有参数,参数必须紧跟在选项后不能以空格隔开。该参数的指针赋给optarg。(这个特性是GNU的扩张)。
optstring是一个字符串,表示可以接受的参数。例如,"a:b:cd",表示可以接受的参数是a,b,c,d,其中,a和b参数后面跟有更多的参数值。(例如:-ahost -b name)

longopts是一个结构的实例
structoption
{
   constchar *name; //name表示的是长参数名
   inthas_arg; //has_arg有3个值,no_argument(或者是0),表示该参数后面不跟参数值
              //required_argument(或者是1),表示该参数后面一定要跟个参数值
              //optional_argument(或者是2),表示该参数后面可以跟,也可以不跟参数值
   int*flag;   //用来决定getopt_long()的返回值是什么。
              //flag是null,则函数会返回与该项option匹配的val值。
   int val;     //和flag联合决定返回值
 };

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中出现的这个选项的参数相同。

=========================================================================

给个例子:
struct option long_options[] ={
          {"a123", required_argument,0, 'a'},
          {"c123", no_argument, 0,'c'},
};
现在,如果命令行的参数是-a123,那么调用getopt_long()将返回字符'a',并且将字符串123由optarg返回(注意注意!字符串123由optarg带回!optarg不需要定义,在getopt.h中已经有定义)。
那么,如果命令行参数是-c,那么调用getopt_long()将返回字符'c',而此时,optarg是null。
最后,当getopt_long()将命令行所有参数全部解析完成后,返回-1。
===========================================================================

以上来自网络,便于学习记忆摘录在这里,下面是自己的理解:
函数原型:int getopt_long(intargc, char* const argv[], 
                     const char*optstring,
                     const struct option*longopts, 
                     int*longindex);
其中optstring为单个字符参数,称为short_opts。
而longopts为多个字符(即一个或多个单词连接)参数,称为long_opts。
参数longindex为longopts数组中的索引返回值。
具体用法参考suricata中main()函数中解析命令行参数:
//短字符参数
charshort_opts[] = "c:TDhi:l:q:d:r:us:S:U:VF:";

//长字符参数

 structoption long_opts[] = {
       {"dump-config", 0, &dump_config, 1},  // getopt_long返回值为0,dump_config保存为1
       {"pfring", optional_argument, 0, 0},  // getopt_long返回值为0
       {"pfring-int", required_argument, 0, 0}, // getopt_long返回值为0,必须有参数
       {"pfring-cluster-id", required_argument, 0,0},
       {"pfring-cluster-type", required_argument, 0,0},
       {"af-packet", optional_argument, 0, 0},
       {"pcap", optional_argument, 0, 0},
       {"pcap-buffer-size", required_argument, 0,0},
       {"unittest-filter", required_argument, 0,'U'},// getopt_long返回值为‘U’,必须有参数
       {"list-app-layer-protos", 0,&list_app_layer_protocols, 1},
       {"list-unittests", 0, &list_unittests,1},
       {"list-cuda-cards", 0, &list_cuda_cards,1},
       {"list-runmodes", 0, &list_runmodes,1},
       {"list-keywords", optional_argument,&list_keywords, 1},
       {"runmode", required_argument, NULL, 0},
       {"engine-analysis", 0, &engine_analysis,1},
       {"pidfile", required_argument, 0, 0},
       {"init-errors-fatal", 0, 0, 0},
       {"fatal-unittests", 0, 0, 0},
       {"user", required_argument, 0, 0},
       {"group", required_argument, 0, 0},
       {"erf-in", required_argument, 0, 0},
       {"dag", required_argument, 0, 0},
       {"napatech", 0, 0, 0},
       {"build-info", 0, &build_info, 1},
       {NULL, 0, NULL, 0}
    };
   // 长字符数组索引
    intoption_index = 0;
 
 // 以下是用法 
 while ((opt= getopt_long(argc, argv, short_opts, long_opts,&option_index)) != -1) 
 {
       switch (opt) 
       {      // case为函数返回值
        case 0:

          if(strcmp((long_opts[option_index]).name , "pfring") == 0 ||                               
 strcmp((long_opts[option_index]).name , "pfring-int") ==0) 
          {
            // TO-DO...
          }
           else if(strcmp((long_opts[option_index]).name , "pcap") ==0) 
          {
            // TO-DO...
          }
          break;
        case 'c':
          break; 
        case 'T':
          break;
        default:
          usage(argv[0]); 
          exit(EXIT_FAILURE);
       }
 }

函数使用

众所周知,C程序的主函数有两个参数,其中,第一个参数是整型,可以获得包括程序名字的参数个数,第二个参数是字符数组指针或字符指针的指针,可以按顺序获得命令行上各个字符串参数。其原形是:

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

或者

int main(int argc, char **argv);

 

如果有一个解析CDR的程序,名叫destroy,负责将一个二进制格式的CDR文件转换为文本文件,输出的文本的样式由另外一个描述文件定义,那么,命令行要求输入的参数就有三个:CDR文件名、输出文件名和描述文件名。其中,前两个参数是必须输入的,第三个的描述文件名可以不输入,程序会自动采用默认的输出样式。很自然,主函数的三个参数就应该这样排列:

./destroy cdr cdr.txt [cdr.desc]

 

这样做在一般情况下不会有太大问题,问题来源于扩展性的需求。如果有一天,用户要求解析程序能够按关键字解析,只有含有关键字的CDR才能够输出。解决方法很简单,只要在参数列表的最后,加上它就可以了。不过,这样就使得原本可选的描述文件名变为必须输入:

./destroy cdr cdr.txt cdr.desc [keyword]

 

因为不改的话,你就不知道,第三个参数究竟是描述文件名,还是关键字。现在还算好办,如果以后陆续有增加参数的需求,关键字也变成必须输入了,这个时候,如果要查找全部CDR,你还得定义一个“特殊的关键字”,告诉程序,把数据统统给我捞出来……

 

有鉴于此,在Unix/Linux的正式的项目上,程序员通常会使用getopt()或者getopt_long()来获得输入的参数。两者的一个区别在于getopt()只支持短格式参数,而getopt_long()既支持短格式参数,又支持长格式参数。

短格式:./destroy -f cdr -o cdr.txt -c cdr.desc -k 123456

长格式:./destroy --file cdr --output cdr.txt --config cdr.desc --keyword 123456

 

引入了getopt()和getopt_long()的项目,设计者可以按需要,方便地增加参数,或者随意地放置参数的先后次序,只需要在程序中判断,哪些参数是必须的就可以了。关于这两个函数的用法,大家可以上网搜索一下,不再累述。附件destroy_linux.c给出了在Linux下使用getopt_long()的实例。

[cpp]
view plaincopy

#include <stdio.h>  
#include <stdlib.h>  
#include <unistd.h>  
#include <getopt.h>  
  
void print_usage(const char *program_name) {  
    printf("%s 1.0.0 (2010-06-13)/n", program_name);  
    printf("This is a program decoding a BER encoded CDR file/n");  
    printf("Usage: %s -f <file_name> -o <output_name> [-c <config_name>] [-k <keyword>]/n", program_name);  
    printf("    -f --file       the CDR file to be decoded/n");  
    printf("    -o --output     the output file in plain text format/n");  
    printf("    -c --config     the description file of the CDR file, if not given, use default configuration/n");  
    printf("    -k --keyword    the keyword to search, if not given, all records will be written into output file/n");  
}  
  
int main(int argc, char *argv[]) {  
    char *file_name = NULL;  
    char *output_name = NULL;  
    char *config_name = NULL;  
    char *keyword = NULL;  
  
    const char *short_opts = "hf:o:c:k:";  
    const struct option long_opts[] = {  
        {"help", no_argument, NULL, 'h'},  
        {"file", required_argument, NULL, 'f'},  
        {"output", required_argument, NULL, 'o'},  
        {"config", required_argument, NULL, 'c'},  
        {"keyword", required_argument, NULL, 'k'},  
        {0, 0, 0, 0}  
    };  
    int hflag = 0;  
  
    int c;  
    opterr = 0;  
  
    while ( (c = getopt_long(argc, argv, short_opts, long_opts, NULL)) != -1 ) {  
        switch ( c ) {  
            case 'h' :  
                hflag = 1;  
                break;  
            case 'f' :  
                file_name = optarg;  
                break;  
            case 'o' :  
                output_name = optarg;  
                break;  
            case 'c' :  
                config_name = optarg;  
                break;  
            case 'k' :  
                keyword = optarg;  
                break;  
            case '?' :  
                if ( optopt == 'f' || optopt == 'o' || optopt == 'c' || optopt == 'k' )  
                    printf("Error: option -%c requires an argument/n", optopt);  
                else if ( isprint(optopt) )  
                    printf("Error: unknown option '-%c'/n", optopt);  
                else  
                    printf("Error: unknown option character '//x%x'/n", optopt);  
                return 1;  
            default :  
                abort();  
        }  
    }  
  
    if ( hflag || argc == 1 ) {  
        print_usage(argv[0]);  
        return 0;  
    }  
    if ( !file_name ) {  
        printf("Error: file name must be specified/n");  
        return 1;  
    }  
    if ( !output_name ) {  
        printf("Error: output name must be specified/n");  
        return 1;  
    }  
  
    // if not setting default, Linux OK, but SunOS core dump  
    if ( !config_name ) config_name = "(null)";  
    if ( !keyword ) keyword = "(null)";  
    printf("Parameters got: file_name = %s, output_name = %s, config_name = %s, keyword = %s/n", file_name, output_name, config_name, keyword);  
    return 0;  
}  

 

另外一个区别是,getopt()几乎通用于所有类Unix系统,而getopt_long()只有在GNU的Unix/Linux下才能用。如果把上述程序放到Tru64上编译,就会出现以下错误:

cc -o destroy destroy_linux.c

cc: Error: destroy_linux.c, line 24: In the initializer for long_opts, an array's element type is incomplete, which precludes its initialization. (incompelinit)

                {"help", no_argument, NULL, 'h'},

----------------^

 

所以,如果一定要在Tru64等非GNU的OS上做到长格式的效果,除了自己另起炉灶之外,基本上只好借助一些跨平台的开源项目了。附件里的getopt_long.c和getopt.h是从opensolaris的网站上抄下来的,是包含在sg3_utils软件包中的程序。sg3_utils具体是什么,我也不知道,据说是一个Linux的开发包,用来直接使用SCSI命令集访问设备。(sg3_utils is a package of utilities for accessing devices that use SCSI
command sets.)反正拿来能用就是了!

[cpp]
view plaincopy

/*  $NetBSD: getopt.h,v 1.7 2005/02/03 04:39:32 perry Exp $ */  
  
/*- 
 * Copyright (c) 2000 The NetBSD Foundation, Inc. 
 * All rights reserved. 
 * 
 * This code is derived from software contributed to The NetBSD Foundation 
 * by Dieter Baron and Thomas Klausner. 
 * 
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions 
 * are met: 
 * 1. Redistributions of source code must retain the above copyright 
 *    notice, this list of conditions and the following disclaimer. 
 * 2. Redistributions in binary form must reproduce the above copyright 
 *    notice, this list of conditions and the following disclaimer in the 
 *    documentation and/or other materials provided with the distribution. 
 * 3. All advertising materials mentioning features or use of this software 
 *    must display the following acknowledgement: 
 *        This product includes software developed by the NetBSD 
 *        Foundation, Inc. and its contributors. 
 * 4. Neither the name of The NetBSD Foundation nor the names of its 
 *    contributors may be used to endorse or promote products derived 
 *    from this software without specific prior written permission. 
 * 
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
 * POSSIBILITY OF SUCH DAMAGE. 
 */  
  
/* 
 * modified May 12, 2005 by Jim Basney <jbasney@ncsa.uiuc.edu> 
 * 
 * removed #include of non-POSIX <sys/cdefs.h> and <sys/featuretest.h> 
 * removed references to _NETBSD_SOURCE and HAVE_NBTOOL_CONFIG_H 
 * added #if !HAVE_GETOPT_LONG 
 * removed __BEGIN_DECLS and __END_DECLS 
 */  
  
#ifndef _MYPROXY_GETOPT_H_  
#define _MYPROXY_GETOPT_H_  
  
#if !HAVE_GETOPT_LONG  
  
#include <unistd.h>  
  
/* 
 * Gnu like getopt_long() and BSD4.4 getsubopt()/optreset extensions 
 */  
#define no_argument        0  
#define required_argument  1  
#define optional_argument  2  
  
extern char *optarg;  
extern int optind;  
extern int optopt;  
extern int opterr;  
  
struct option {  
    /* name of long option */  
    const char *name;  
    /* 
     * one of no_argument, required_argument, and optional_argument: 
     * whether option takes an argument 
     */  
    int has_arg;  
    /* if not NULL, set *flag to val when option found */  
    int *flag;  
    /* if flag not NULL, value to set *flag to; else return value */  
    int val;  
};  
  
int getopt_long(int, char * const *, const char *,  
    const struct option *, int *);  
   
#endif /* !HAVE_GETOPT_LONG */  
  
#endif /* !_MYPROXY_GETOPT_H_ */  

[cpp]
view plaincopy

/*  $NetBSD: getopt_long.c,v 1.17 2004/06/20 22:20:15 jmc Exp $ */  
  
/*- 
 * Copyright (c) 2000 The NetBSD Foundation, Inc. 
 * All rights reserved. 
 * 
 * This code is derived from software contributed to The NetBSD Foundation 
 * by Dieter Baron and Thomas Klausner. 
 * 
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions 
 * are met: 
 * 1. Redistributions of source code must retain the above copyright 
 *    notice, this list of conditions and the following disclaimer. 
 * 2. Redistributions in binary form must reproduce the above copyright 
 *    notice, this list of conditions and the following disclaimer in the 
 *    documentation and/or other materials provided with the distribution. 
 * 3. All advertising materials mentioning features or use of this software 
 *    must display the following acknowledgement: 
 *        This product includes software developed by the NetBSD 
 *        Foundation, Inc. and its contributors. 
 * 4. Neither the name of The NetBSD Foundation nor the names of its 
 *    contributors may be used to endorse or promote products derived 
 *    from this software without specific prior written permission. 
 * 
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
 * POSSIBILITY OF SUCH DAMAGE. 
 */  
  
/* 
 * modified May 12, 2005 by Jim Basney <jbasney@ncsa.uiuc.edu> 
 * 
 * removed #include of non-POSIX <sys/cdefs.h> <err.h> 
 * removed #include of "namespace.h" 
 * use local "port_getopt.h" instead of <getopt.h> 
 * removed REPLACE_GETOPT and HAVE_NBTOOL_CONFIG_H sections 
 * removed __P() from function declarations 
 * use ANSI C function parameter lists 
 * removed optreset support 
 * replace _DIAGASSERT() with assert() 
 * replace non-POSIX warnx(...) with fprintf(stderr, ...) 
 * added extern declarations for optarg, optind, opterr, and optopt 
 */  
  
#if defined(LIBC_SCCS) && !defined(lint)  
__RCSID("$NetBSD: getopt_long.c,v 1.17 2004/06/20 22:20:15 jmc Exp $");  
#endif /* LIBC_SCCS and not lint */  
  
#include <assert.h>  
#include <errno.h>  
#include "getopt.h"  
#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
  
#ifdef __weak_alias  
__weak_alias(getopt_long,_getopt_long)  
#endif  
  
#if !HAVE_GETOPT_LONG  
#define IGNORE_FIRST    (*options == '-' || *options == '+')  
#define PRINT_ERROR ((opterr) && ((*options != ':') /  
                      || (IGNORE_FIRST && options[1] != ':')))  
#define IS_POSIXLY_CORRECT (getenv("POSIXLY_CORRECT") != NULL)  
#define PERMUTE         (!IS_POSIXLY_CORRECT && !IGNORE_FIRST)  
/* XXX: GNU ignores PC if *options == '-' */  
#define IN_ORDER        (!IS_POSIXLY_CORRECT && *options == '-')  
  
/* return values */  
#define BADCH   (int)'?'  
#define BADARG      ((IGNORE_FIRST && options[1] == ':') /  
             || (*options == ':') ? (int)':' : (int)'?')  
#define INORDER (int)1  
  
#define EMSG    ""  
  
extern char *optarg;  
extern int optind, opterr, optopt;  
  
static int getopt_internal (int, char * const *, const char *);  
static int gcd (int, int);  
static void permute_args (int, int, int, char * const *);  
  
static char *place = EMSG; /* option letter processing */  
  
static int nonopt_start = -1; /* first non option argument (for permute) */  
static int nonopt_end = -1;   /* first option after non options (for permute) */  
  
/* Error messages */  
static const char recargchar[] = "option requires an argument -- %c";  
static const char recargstring[] = "option requires an argument -- %s";  
static const char ambig[] = "ambiguous option -- %.*s";  
static const char noarg[] = "option doesn't take an argument -- %.*s";  
static const char illoptchar[] = "unknown option -- %c";  
static const char illoptstring[] = "unknown option -- %s";  
  
  
/* 
 * Compute the greatest common divisor of a and b. 
 */  
static int  
gcd(int a, int b)  
{  
    int c;  
  
    c = a % b;  
    while (c != 0) {  
        a = b;  
        b = c;  
        c = a % b;  
    }  
         
    return b;  
}  
  
/* 
 * Exchange the block from nonopt_start to nonopt_end with the block 
 * from nonopt_end to opt_end (keeping the same order of arguments 
 * in each block). 
 */  
static void  
permute_args(int panonopt_start, int panonopt_end, int opt_end,  
         char * const *nargv)  
{  
    int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;  
    char *swap;  
  
    assert(nargv != NULL);  
  
    /* 
     * compute lengths of blocks and number and size of cycles 
     */  
    nnonopts = panonopt_end - panonopt_start;  
    nopts = opt_end - panonopt_end;  
    ncycle = gcd(nnonopts, nopts);  
    cyclelen = (opt_end - panonopt_start) / ncycle;  
  
    for (i = 0; i < ncycle; i++) {  
        cstart = panonopt_end+i;  
        pos = cstart;  
        for (j = 0; j < cyclelen; j++) {  
            if (pos >= panonopt_end)  
                pos -= nnonopts;  
            else  
                pos += nopts;  
            swap = nargv[pos];  
            /* LINTED const cast */  
            ((char **) nargv)[pos] = nargv[cstart];  
            /* LINTED const cast */  
            ((char **)nargv)[cstart] = swap;  
        }  
    }  
}  
  
/* 
 * getopt_internal -- 
 *  Parse argc/argv argument vector.  Called by user level routines. 
 *  Returns -2 if -- is found (can be long option or end of options marker). 
 */  
static int  
getopt_internal(int nargc, char * const *nargv, const char *options)  
{  
    char *oli;              /* option letter list index */  
    int optchar;  
  
    assert(nargv != NULL);  
    assert(options != NULL);  
  
    optarg = NULL;  
  
    /* 
     * XXX Some programs (like rsyncd) expect to be able to 
     * XXX re-initialize optind to 0 and have getopt_long(3) 
     * XXX properly function again.  Work around this braindamage. 
     */  
    if (optind == 0)  
        optind = 1;  
  
start:  
    if (!*place) {                      /* update scanning pointer */  
        if (optind >= nargc) {          /* end of argument vector */  
            place = EMSG;  
            if (nonopt_end != -1) {  
                /* do permutation, if we have to */  
                permute_args(nonopt_start, nonopt_end,  
                    optind, nargv);  
                optind -= nonopt_end - nonopt_start;  
            }  
            else if (nonopt_start != -1) {  
                /* 
                 * If we skipped non-options, set optind 
                 * to the first of them. 
                 */  
                optind = nonopt_start;  
            }  
            nonopt_start = nonopt_end = -1;  
            return -1;  
        }  
        if ((*(place = nargv[optind]) != '-')  
            || (place[1] == '/0')) {    /* found non-option */  
            place = EMSG;  
            if (IN_ORDER) {  
                /* 
                 * GNU extension:  
                 * return non-option as argument to option 1 
                 */  
                optarg = nargv[optind++];  
                return INORDER;  
            }  
            if (!PERMUTE) {  
                /* 
                 * if no permutation wanted, stop parsing 
                 * at first non-option 
                 */  
                return -1;  
            }  
            /* do permutation */  
            if (nonopt_start == -1)  
                nonopt_start = optind;  
            else if (nonopt_end != -1) {  
                permute_args(nonopt_start, nonopt_end,  
                    optind, nargv);  
                nonopt_start = optind -  
                    (nonopt_end - nonopt_start);  
                nonopt_end = -1;  
            }  
            optind++;  
            /* process next argument */  
            goto start;  
        }  
        if (nonopt_start != -1 && nonopt_end == -1)  
            nonopt_end = optind;  
        if (place[1] && *++place == '-') {  /* found "--" */  
            place++;  
            return -2;  
        }  
    }  
    if ((optchar = (int)*place++) == (int)':' ||  
        (oli = strchr(options + (IGNORE_FIRST ? 1 : 0), optchar)) == NULL) {  
        /* option letter unknown or ':' */  
        if (!*place)  
            ++optind;  
        if (PRINT_ERROR)  
            fprintf(stderr, illoptchar, optchar);  
        optopt = optchar;  
        return BADCH;  
    }  
    if (optchar == 'W' && oli[1] == ';') {      /* -W long-option */  
        /* XXX: what if no long options provided (called by getopt)? */  
        if (*place)   
            return -2;  
  
        if (++optind >= nargc) { /* no arg */  
            place = EMSG;  
            if (PRINT_ERROR)  
                fprintf(stderr, recargchar, optchar);  
            optopt = optchar;  
            return BADARG;  
        } else              /* white space */  
            place = nargv[optind];  
        /* 
         * Handle -W arg the same as --arg (which causes getopt to 
         * stop parsing). 
         */  
        return -2;  
    }  
    if (*++oli != ':') {            /* doesn't take argument */  
        if (!*place)  
            ++optind;  
    } else {                /* takes (optional) argument */  
        optarg = NULL;  
        if (*place)         /* no white space */  
            optarg = place;  
        /* XXX: disable test for :: if PC? (GNU doesn't) */  
        else if (oli[1] != ':') {   /* arg not optional */  
            if (++optind >= nargc) { /* no arg */  
                place = EMSG;  
                if (PRINT_ERROR)  
                    fprintf(stderr, recargchar, optchar);  
                optopt = optchar;  
                return BADARG;  
            } else  
                optarg = nargv[optind];  
        }  
        place = EMSG;  
        ++optind;  
    }  
    /* dump back option letter */  
    return optchar;  
}  
  
/* 
 * getopt_long -- 
 *  Parse argc/argv argument vector. 
 */  
int  
getopt_long(int nargc, char * const *nargv, const char *options,  
        const struct option *long_options, int *idx)  
{  
    int retval;  
  
    assert(nargv != NULL);  
    assert(options != NULL);  
    assert(long_options != NULL);  
    /* idx may be NULL */  
  
    if ((retval = getopt_internal(nargc, nargv, options)) == -2) {  
        char *current_argv, *has_equal;  
        size_t current_argv_len;  
        int i, match;  
  
        current_argv = place;  
        match = -1;  
  
        optind++;  
        place = EMSG;  
  
        if (*current_argv == '/0') {        /* found "--" */  
            /* 
             * We found an option (--), so if we skipped 
             * non-options, we have to permute. 
             */  
            if (nonopt_end != -1) {  
                permute_args(nonopt_start, nonopt_end,  
                    optind, nargv);  
                optind -= nonopt_end - nonopt_start;  
            }  
            nonopt_start = nonopt_end = -1;  
            return -1;  
        }  
        if ((has_equal = strchr(current_argv, '=')) != NULL) {  
            /* argument found (--option=arg) */  
            current_argv_len = has_equal - current_argv;  
            has_equal++;  
        } else  
            current_argv_len = strlen(current_argv);  
          
        for (i = 0; long_options[i].name; i++) {  
            /* find matching long option */  
            if (strncmp(current_argv, long_options[i].name,  
                current_argv_len))  
                continue;  
  
            if (strlen(long_options[i].name) ==  
                (unsigned)current_argv_len) {  
                /* exact match */  
                match = i;  
                break;  
            }  
            if (match == -1)        /* partial match */  
                match = i;  
            else {  
                /* ambiguous abbreviation */  
                if (PRINT_ERROR)  
                    fprintf(stderr, ambig, (int)current_argv_len,  
                         current_argv);  
                optopt = 0;  
                return BADCH;  
            }  
        }  
        if (match != -1) {          /* option found */  
                if (long_options[match].has_arg == no_argument  
                && has_equal) {  
                if (PRINT_ERROR)  
                    fprintf(stderr, noarg, (int)current_argv_len,  
                         current_argv);  
                /* 
                 * XXX: GNU sets optopt to val regardless of 
                 * flag 
                 */  
                if (long_options[match].flag == NULL)  
                    optopt = long_options[match].val;  
                else  
                    optopt = 0;  
                return BADARG;  
            }  
            if (long_options[match].has_arg == required_argument ||  
                long_options[match].has_arg == optional_argument) {  
                if (has_equal)  
                    optarg = has_equal;  
                else if (long_options[match].has_arg ==  
                    required_argument) {  
                    /* 
                     * optional argument doesn't use 
                     * next nargv 
                     */  
                    optarg = nargv[optind++];  
                }  
            }  
            if ((long_options[match].has_arg == required_argument)  
                && (optarg == NULL)) {  
                /* 
                 * Missing argument; leading ':' 
                 * indicates no error should be generated 
                 */  
                if (PRINT_ERROR)  
                    fprintf(stderr, recargstring, current_argv);  
                /* 
                 * XXX: GNU sets optopt to val regardless 
                 * of flag 
                 */  
                if (long_options[match].flag == NULL)  
                    optopt = long_options[match].val;  
                else  
                    optopt = 0;  
                --optind;  
                return BADARG;  
            }  
        } else {            /* unknown option */  
            if (PRINT_ERROR)  
                fprintf(stderr, illoptstring, current_argv);  
            optopt = 0;  
            return BADCH;  
        }  
        if (long_options[match].flag) {  
            *long_options[match].flag = long_options[match].val;  
            retval = 0;  
        } else   
            retval = long_options[match].val;  
        if (idx)  
            *idx = match;  
    }  
    return retval;  
}  
#endif /* !GETOPT_LONG */  

 

拿过来后,把他们放到与destroy_linux.c同一目录下,只需要把destroy_linux.c的头文件改一个地方,#include <getopt.h>改为#include “getopt.h”,就能够编译运行了。而且,这样改好后,不仅在Tru64上能运行,在Linux、SunOS上也能运行。

[cpp]
view plaincopy

#include <stdio.h>  
#include <stdlib.h>  
#include <unistd.h>  
#include "getopt.h"  
  
void print_usage(const char *program_name) {  
    printf("%s 1.0.0 (2010-06-13)/n", program_name);  
    printf("This is a program decoding a BER encoded CDR file/n");  
    printf("Usage: %s -f <file_name> -o <output_name> [-c <config_name>] [-k <keyword>]/n", program_name);  
    printf("    -f --file       the CDR file to be decoded/n");  
    printf("    -o --output     the output file in plain text format/n");  
    printf("    -c --config     the description file of the CDR file, if not given, use default configuration/n");  
    printf("    -k --keyword    the keyword to search, if not given, all records will be written into output file/n");  
}  
  
int main(int argc, char *argv[]) {  
    char *file_name = NULL;  
    char *output_name = NULL;  
    char *config_name = NULL;  
    char *keyword = NULL;  
  
    const char *short_opts = "hf:o:c:k:";  
    const struct option long_opts[] = {  
        {"help", no_argument, NULL, 'h'},  
        {"file", required_argument, NULL, 'f'},  
        {"output", required_argument, NULL, 'o'},  
        {"config", required_argument, NULL, 'c'},  
        {"keyword", required_argument, NULL, 'k'},  
        {0, 0, 0, 0}  
    };  
    int hflag = 0;  
  
    int c;  
    opterr = 0;  
  
    while ( (c = getopt_long(argc, argv, short_opts, long_opts, NULL)) != -1 ) {  
        switch ( c ) {  
            case 'h' :  
                hflag = 1;  
                break;  
            case 'f' :  
                file_name = optarg;  
                break;  
            case 'o' :  
                output_name = optarg;  
                break;  
            case 'c' :  
                config_name = optarg;  
                break;  
            case 'k' :  
                keyword = optarg;  
                break;  
            case '?' :  
                if ( optopt == 'f' || optopt == 'o' || optopt == 'c' || optopt == 'k' )  
                    printf("Error: option -%c requires an argument/n", optopt);  
                else if ( isprint(optopt) )  
                    printf("Error: unknown option '-%c'/n", optopt);  
                else  
                    printf("Error: unknown option character '//x%x'/n", optopt);  
                return 1;  
            default :  
                abort();  
        }  
    }  
  
    if ( hflag || argc == 1 ) {  
        print_usage(argv[0]);  
        return 0;  
    }  
    if ( !file_name ) {  
        printf("Error: file name must be specified/n");  
        return 1;  
    }  
    if ( !output_name ) {  
        printf("Error: output name must be specified/n");  
        return 1;  
    }  
  
    // if not setting default, Linux OK, but SunOS core dump  
    if ( !config_name ) config_name = "(null)";  
    if ( !keyword ) keyword = "(null)";  
    printf("Parameters got: file_name = %s, output_name = %s, config_name = %s, keyword = %s/n", file_name, output_name, config_name, keyword);  
    return 0;  
}  

 

Linux下编译

-bash-3.2$ gcc -o destroy destroy.c getopt_long.c

短格式,全部输入

-bash-3.2$ ./destroy -f aaa -o aaa.txt -c ccc -k 222

Parameters got: file_name = aaa, output_name = aaa.txt, config_name = ccc, keyword = 222

前两个长格式,后两个短格式

-bash-3.2$ ./destroy --file aaa --output aaa.txt -c ccc -k 222

Parameters got: file_name = aaa, output_name = aaa.txt, config_name = ccc, keyword = 222

漏掉一个必须输入的参数会报错

-bash-3.2$ ./destroy -output aaa.txt

Error: file name must be specified

次序随意,长短混用

-bash-3.2$ ./destroy -c ccc -o aaa.txt -k 222 --file aaa

Parameters got: file_name = aaa, output_name = aaa.txt, config_name = ccc, keyword = 222

 

题外话,#include <filename.h>与#include “filename.h”有什么区别,是面试C程序员经常问到的一个问题。答案大家都知道了,#include <filename.h>,编译器从标准库路径搜索filename.h,而#include “filename.h”,编译器从用户的工作路径搜索filename.h。

 

此外,网上也有人说从glibc(http://sourceware.org/glibc/)上把getopt.h、getopt.c和getoptl.c拿过来也能够用。我也试过,但是不清楚什么原因不成功。

 

在这个小实验的过程中,还发现了C语言在各个OS下的一些细小差异,比如destroy.c里,79行到82行:

// if not setting default, Linux OK, but SunOS core dump

if ( !config_name ) config_name = "(null)";

if ( !keyword ) keyword = "(null)";

printf("Parameters got: file_name = %s, output_name = %s, config_name = %s, keyword = %s/n", file_name, output_name, config_name, keyword);

 

如果在81行和82行不设置空指针的默认值,Linux和Tru64都会自动帮你转换而避免运行时错误,但是SunOS不会,它会死给你看。

./destroy -f aaa -o aaa.txt

Segmentation Fault (core dumped)

 

再比如第62行的abort()在头文件stdlib.h中定义,如果不包含此文件,SunOS与Tru64编译都没问题,Linux编译时会警告:

warning: incompatible implicit declaration of built-in function abort

 

由此看来,虽然C也公认是可移植性比较好的语言,但是在跨平台的项目中,也应该注意这些微小的差别。

转自:http://blog.csdn.net/lanmanck/article/details/11999345
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: