一、前言

在学习一些项目代码时,尤其涉及到命令行传参的代码,经常遇到getopt相关的函数,对这一类函数可以说是既陌生又熟悉。陌生是因为不知道它是干啥的,熟悉呢,是因为经常遇到。于是乎在追踪了多天ipsec配置文件解析流程之后,准备学习下这一类命令行解析利器。

这么多命令行参数,需要解析,想象都让人头大,如果再没有一个好的解析方式,那就雪上加霜了。辛亏有了一类命令行解析函数,可以让这些解析操作变得容易一点点。下面就简单的介绍下这一类函数,主要有getopt、getopt_long、getopt_long_only。

Man 手册信息如下:

NAME
       getopt, getopt_long, getopt_long_only, optarg, optind, opterr, optopt - Parse command-line options

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

先从最简单的,最基本的getopt函数开始。然后再介绍增强版的getopt_long函数。

二、getopt函数

2.1 函数简介:

  int getopt(int argc, char * const argv[], const char *optstring);

各项参数解释如下:

函数功能

用来解析命令行选项参数,只适用于短选项:-d /root; 不能解析长选项--arch, --help等

Argc

通过命令行传给main函数参数的个数

argv

通过命令行传给main函数参数组成的字符串指针数组

optstring

选项字符串。用来告诉getopt可以解析哪些选项,哪些选项需要参数以及函数返回值等(字符后面紧跟一个冒号则需要参数,而两个则表示参数可选)

返回值

如果成功返回选项字母;命令行解析完毕返回-1;如果选项未定义,则提示错误信息,并返回'?';如果参数缺失,第一个字符是':'则返回':', 否则返回'?'

对于optstring字符串格式做一个简单说明:

单个字符,表示没有参数

单个字符紧跟1个冒号,表示必须有参数,格式:-d xxx或者-dxxx

单个字符紧跟2个冒号,表示参数可选,格式:-dxxx

Char *optstring="ab:c::";

说明:此选项指定了三个选项,分别为'a'、'b'、'c'。其中

a选项无需参数,格式:-a即可

b选项必须有参数,格式:-d xxx

c选项参数可选,格式:-c ooo

在此类函数中,需要几个重要的变量共同配合才能完成命令行参数解析工作:

变量名称

作用

Char *optarg

指向当前选项参数的指针

Int optind

用来记录当前已遍历选项的索引,方便下次调用时直接找到下一个选项

Int opterr

正常情况,解析失败会有错误信息,如果不需要此错误信息,将opterr置0即可

Int optopt

最后一个未知选项

2.2 举例说明:

代码如下:


/*************************************************************************
             > File Name: getopt.c
             > Author: Toney
             > Mail: vip_13031075266@163.com
       > Created Time: 2021年01月30日 星期六 21时50分45秒
 ************************************************************************/

#include <stdio.h>
#include <unistd.h>
#include <getopt.h>

void main(int argc, char **argv)
{
        int flags, opt;

        while((opt = getopt(argc, argv, "ab:c::")) != -1){

                switch(opt){
                case 'a':
                        printf("Input %d parameter is -a=%s\n", optind, optarg);
                        break;
                case 'b':
                        printf("Input %d parameter is -b=%s\n", optind, optarg);
                        break;
                case 'c':
                        printf("Input %d parameter is -c=%s\n", optind, optarg);
                        break;
                default:
                        printf("Not match!!!");
                }
        }

        return ;

}

 

命令行解析函数:getopt_long、getopt_故障

三、getopt_long函数

3.1 函数简介:

int getopt_long(int argc, char * const argv[], const char *optstring,const struct option *longopts, int *longindex);

各项参数解释如下:

函数功能

用来解析命令行选项参数,除了getopt函数支持的短选项,还支持长选项,如--help, --prefix

Argc

通过命令行传给main函数参数的个数

argv

通过命令行传给main函数参数组成的字符串指针数组

optstring

选项字符串。用来告诉getopt可以解析哪些选项,哪些选项需要参数以及函数返回值等(字符后面紧跟一个冒号则需要参数,而两个则表示参数可选)

longopts

长选项参数的名称、属性、以及解析后的返回值等结构信息

longindex

用来记录解析到的当前长选项的索引,也就是longopts的下标。

返回值

对于短选项,返回值同getopt函数;对于长选项,如果flag是NULL,返回val,否则返回0;对于错误情况返回值同getopt函数

Struct option结构体:

struct option {
const char  *name;       /* 参数名称 */
int          has_arg;    /* 指明是否带有参数 */
int          *flag;      /* flag=NULL时,返回value;不为空时,*flag=val,返回0 */
int          val;        /* 用于指定函数找到选项的返回值或flag非空时指定*flag的值 */
}; 

其中has_arg类型取值有如下几种:

No_argument

不需要参数

Required_argument

必须带参数

Optional_argument

参数可选

在此类函数中,需要几个重要的变量共同配合才能完成命令行参数解析工作:

变量名称

作用

Char *optarg

指向当前选项参数的指针

Int optind

用来记录当前已遍历选项的索引,方便下次调用时直接找到下一个选项

Int opterr

正常情况,解析失败会有错误信息,如果不需要此错误信息,将opterr置0即可

Int optopt

最后一个未知选项

3.2 ipsec项目中的一个例子:

先看看在openswan源码中,有关使用getopt_long解析命令行的一个例子(各位把住坐稳了):

static const struct option long_opts[] = {
#   define OO	OPTION_OFFSET
    /* name, has_arg, flag, val */

    { "help", no_argument, NULL, 'h' },
    { "version", no_argument, NULL, 'v' },
    { "optionsfrom", required_argument, NULL, '+' },
    { "label", required_argument, NULL, 'l' },

    { "ctlbase", required_argument, NULL, OPT_CTLBASE + OO },
    { "name", required_argument, NULL, OPT_NAME + OO },
    { "connalias", required_argument, NULL, OPT_CONNALIAS + OO },

    { "keyid", required_argument, NULL, OPT_KEYID + OO },
    { "addkey", no_argument, NULL, OPT_ADDKEY + OO },
    { "pubkeyrsa", required_argument, NULL, OPT_PUBKEYRSA + OO },

    { "myid", required_argument, NULL, OPT_MYID + OO },

    { "route", no_argument, NULL, OPT_ROUTE + OO },
    { "unroute", no_argument, NULL, OPT_UNROUTE + OO },

    { "initiate", no_argument, NULL, OPT_INITIATE + OO },
    { "terminate", no_argument, NULL, OPT_TERMINATE + OO },
    { "delete", no_argument, NULL, OPT_DELETE + OO },
    { "deletestate", required_argument, NULL, OPT_DELETESTATE + OO + NUMERIC_ARG },
    { "crash", required_argument, NULL, OPT_DELETECRASH + OO },
    { "listen", no_argument, NULL, OPT_LISTEN + OO },
    { "unlisten", no_argument, NULL, OPT_UNLISTEN + OO },
    { "purgeocsp", no_argument, NULL, OPT_PURGEOCSP + OO },

    { "rereadsecrets", no_argument, NULL, OPT_REREADSECRETS + OO },
    { "rereadcacerts", no_argument, NULL, OPT_REREADCACERTS + OO },
    { "rereadaacerts", no_argument, NULL, OPT_REREADAACERTS + OO },
    { "rereadocspcerts", no_argument, NULL, OPT_REREADOCSPCERTS + OO },
    { "rereadacerts", no_argument, NULL, OPT_REREADACERTS + OO },

    { "rereadcrls", no_argument, NULL, OPT_REREADCRLS + OO },
    { "rereadall", no_argument, NULL, OPT_REREADALL + OO },
    { "status", no_argument, NULL, OPT_STATUS + OO },
    { "shutdown", no_argument, NULL, OPT_SHUTDOWN + OO },
    { "xauthname", required_argument, NULL, OPT_XAUTHNAME + OO },
    { "xauthuser", required_argument, NULL, OPT_XAUTHNAME + OO },
    { "xauthpass", required_argument, NULL, OPT_XAUTHPASS + OO },
    { "tpmeval",   required_argument, NULL, OPT_TPMEVAL   + OO },

    { "oppohere", required_argument, NULL, OPT_OPPO_HERE + OO },
    { "oppothere", required_argument, NULL, OPT_OPPO_THERE + OO },

    { "asynchronous", no_argument, NULL, OPT_ASYNC + OO },

    /* list options */

    { "utc", no_argument, NULL, LST_UTC + OO },
    { "checkpubkeys", no_argument, NULL, LST_CHECKPUBKEYS + OO },
    { "listpubkeys", no_argument, NULL, LST_PUBKEYS + OO },
    { "listcerts", no_argument, NULL, LST_CERTS + OO },
    { "listcacerts", no_argument, NULL, LST_CACERTS + OO },
    { "listacerts", no_argument, NULL, LST_ACERTS + OO },
    { "listaacerts", no_argument, NULL, LST_AACERTS + OO },
    { "listocspcerts", no_argument, NULL, LST_OCSPCERTS + OO },
    { "listgroups", no_argument, NULL, LST_GROUPS + OO },
    { "listcrls", no_argument, NULL, LST_CRLS + OO },
    { "listocsp", no_argument, NULL, LST_OCSP + OO },
    { "listpsks", no_argument, NULL, LST_PSKS + OO },
    { "listevents", no_argument, NULL, LST_EVENTS + OO },
    { "listpairs",     no_argument, NULL, LST_HOSTPAIRS + OO },
    { "listhostpairs", no_argument, NULL, LST_HOSTPAIRS + OO },
    { "listall", no_argument, NULL, LST_ALL + OO },


    /* options for an end description */

    { "host", required_argument, NULL, END_HOST + OO },
    { "id", required_argument, NULL, END_ID + OO },
    { "cert", required_argument, NULL, END_CERT + OO },
    { "ca", required_argument, NULL, END_CA + OO },
    { "groups", required_argument, NULL, END_GROUPS + OO },
    { "ikeport", required_argument, NULL, END_IKEPORT + OO + NUMERIC_ARG },
    { "nexthop", required_argument, NULL, END_NEXTHOP + OO },
    { "client", required_argument, NULL, END_CLIENT + OO },
    { "clientwithin", required_argument, NULL, END_CLIENTWITHIN + OO },
    { "clientprotoport", required_argument, NULL, END_CLIENTPROTOPORT + OO },
    { "dnskeyondemand", no_argument, NULL, END_DNSKEYONDEMAND + OO },
    { "srcip",  required_argument, NULL, END_SRCIP + OO },
    { "updown", required_argument, NULL, END_UPDOWN + OO },
    { "tundev", required_argument, NULL, END_TUNDEV + OO + NUMERIC_ARG },


    /* options for a connection description */

    { "to", no_argument, NULL, CD_TO + OO },

    { "psk", no_argument, NULL, CD_PSK + OO },
    { "rsasig", no_argument, NULL, CD_RSASIG + OO },

    { "encrypt", no_argument, NULL, CD_ENCRYPT + OO },
    { "authenticate", no_argument, NULL, CD_AUTHENTICATE + OO },
    { "compress",  no_argument, NULL, CD_COMPRESS + OO },
    { "overlapip", no_argument, NULL, CD_OVERLAPIP + OO },
    { "tunnel", no_argument, NULL, CD_TUNNEL + OO },
    { "tunnelipv4", no_argument, NULL, CD_TUNNELIPV4 + OO },
    { "tunnelipv6", no_argument, NULL, CD_TUNNELIPV6 + OO },
    { "pfs", no_argument, NULL, CD_PFS + OO },
    { "sha2_truncbug", no_argument, NULL, CD_SHA2_TRUNCBUG + OO },
    { "aggrmode", no_argument, NULL, CD_AGGRESSIVE + OO },
    { "disablearrivalcheck", no_argument, NULL, CD_DISABLEARRIVALCHECK + OO },
    { "initiateontraffic", no_argument, NULL
	, CD_SHUNT0 + (POLICY_SHUNT_TRAP >> POLICY_SHUNT_SHIFT << AUX_SHIFT) + OO },
    { "pass", no_argument, NULL
	, CD_SHUNT0 + (POLICY_SHUNT_PASS >> POLICY_SHUNT_SHIFT << AUX_SHIFT) + OO },
    { "drop", no_argument, NULL
	, CD_SHUNT0 + (POLICY_SHUNT_DROP >> POLICY_SHUNT_SHIFT << AUX_SHIFT) + OO },
    { "reject", no_argument, NULL
	, CD_SHUNT0 + (POLICY_SHUNT_REJECT >> POLICY_SHUNT_SHIFT << AUX_SHIFT) + OO },
    { "failnone", no_argument, NULL
	, CD_FAIL0 + (POLICY_FAIL_NONE >> POLICY_FAIL_SHIFT << AUX_SHIFT) + OO },
    { "failpass", no_argument, NULL
	, CD_FAIL0 + (POLICY_FAIL_PASS >> POLICY_FAIL_SHIFT << AUX_SHIFT) + OO },
    { "faildrop", no_argument, NULL
	, CD_FAIL0 + (POLICY_FAIL_DROP >> POLICY_FAIL_SHIFT << AUX_SHIFT) + OO },
    { "failreject", no_argument, NULL
	, CD_FAIL0 + (POLICY_FAIL_REJECT >> POLICY_FAIL_SHIFT << AUX_SHIFT) + OO },
    { "dontrekey", no_argument, NULL, CD_DONT_REKEY + OO },
    { "forceencaps", no_argument, NULL, CD_FORCEENCAPS + OO },
    { "dpddelay", required_argument, NULL, CD_DPDDELAY + OO + NUMERIC_ARG },
    { "dpdtimeout", required_argument, NULL, CD_DPDTIMEOUT + OO + NUMERIC_ARG },
    { "dpdaction", required_argument, NULL, CD_DPDACTION + OO },
#ifdef XAUTH
    { "xauth", no_argument, NULL, END_XAUTHSERVER + OO },
    { "xauthserver", no_argument, NULL, END_XAUTHSERVER + OO },
    { "xauthclient", no_argument, NULL, END_XAUTHCLIENT + OO },
#endif
#ifdef MODECFG
    { "modecfgpull",   no_argument, NULL, CD_MODECFGPULL + OO },
    { "modecfgserver", no_argument, NULL, END_MODECFGSERVER + OO },
    { "modecfgclient", no_argument, NULL, END_MODECFGCLIENT + OO },
#ifdef MODECFG_DNSWINS
    { "modecfgdns1", required_argument, NULL, CD_MODECFGDNS1 + OO },
    { "modecfgdns2", required_argument, NULL, CD_MODECFGDNS2 + OO },
    { "modecfgwins1", required_argument, NULL, CD_MODECFGWINS1 + OO },
    { "modecfgwins2", required_argument, NULL, CD_MODECFGWINS2 + OO },
    { "modeconfigserver", no_argument, NULL, END_MODECFGSERVER + OO },
    { "modeconfigclient", no_argument, NULL, END_MODECFGCLIENT + OO },
#endif
#endif
    { "metric", required_argument, NULL, CD_METRIC + OO + NUMERIC_ARG },
    { "mtu", required_argument, NULL, CD_CONNMTU + OO + NUMERIC_ARG },
    { "sendcert", required_argument, NULL, END_SENDCERT + OO },
    { "certtype", required_argument, NULL, END_CERTTYPE + OO + NUMERIC_ARG },
    { "ipv4", no_argument, NULL, CD_CONNIPV4 + OO },
    { "ipv6", no_argument, NULL, CD_CONNIPV6 + OO },

    { "ikelifetime", required_argument, NULL, CD_IKELIFETIME + OO + NUMERIC_ARG },
    { "ipseclifetime", required_argument, NULL, CD_IPSECLIFETIME + OO + NUMERIC_ARG },
    { "rekeymargin", required_argument, NULL, CD_RKMARGIN + OO + NUMERIC_ARG },
    { "rekeywindow", required_argument, NULL, CD_RKMARGIN + OO + NUMERIC_ARG },	/* OBSOLETE */
    { "rekeyfuzz", required_argument, NULL, CD_RKFUZZ + OO + NUMERIC_ARG },
    { "keyingtries", required_argument, NULL, CD_KTRIES + OO + NUMERIC_ARG },
    { "ike",    required_argument, NULL, CD_IKE + OO },
    { "ikealg", required_argument, NULL, CD_IKE + OO },
    { "pfsgroup", required_argument, NULL, CD_PFSGROUP + OO },
    { "esp", required_argument, NULL, CD_ESP + OO },
    { "remote_peer_type", required_argument, NULL, CD_REMOTEPEERTYPE + OO},
#ifdef HAVE_NM
    { "nm_configured", no_argument, NULL, CD_NMCONFIGURED + OO},
#endif
#ifdef HAVE_LABELED_IPSEC
    { "loopback", no_argument, NULL, CD_LOOPBACK + OO},
    { "labeledipsec", no_argument, NULL, CD_LABELED_IPSEC + OO},
    { "policylabel", required_argument, NULL, CD_POLICY_LABEL + OO },
#endif
#ifdef DEBUG
    { "debug-none", no_argument, NULL, DBGOPT_NONE + OO },
    { "debug-all", no_argument, NULL, DBGOPT_ALL + OO },
    { "debug-raw", no_argument, NULL, DBGOPT_RAW + OO },
    { "debug-crypt", no_argument, NULL, DBGOPT_CRYPT + OO },
    { "debug-parsing", no_argument, NULL, DBGOPT_PARSING + OO },
    { "debug-emitting", no_argument, NULL, DBGOPT_EMITTING + OO },
    { "debug-control", no_argument, NULL, DBGOPT_CONTROL + OO },
    { "debug-lifecycle", no_argument, NULL, DBGOPT_LIFECYCLE + OO },
    { "debug-klips",  no_argument, NULL, DBGOPT_KLIPS + OO },
    { "debug-netkey", no_argument, NULL, DBGOPT_KLIPS + OO },
    { "debug-xfrm",   no_argument, NULL, DBGOPT_KLIPS + OO },
    { "debug-dns", no_argument, NULL, DBGOPT_DNS + OO },
    { "debug-oppo", no_argument, NULL, DBGOPT_OPPO + OO },
    { "debug-oppoinfo", no_argument, NULL, DBGOPT_OPPOINFO + OO },
    { "debug-whackwatch",  no_argument, NULL, DBGOPT_WHACKWATCH + OO },
    { "debug-controlmore", no_argument, NULL, DBGOPT_CONTROLMORE + OO },
    { "debug-pfkey",   no_argument, NULL, DBGOPT_PFKEY + OO },
    { "debug-nattraversal", no_argument, NULL, DBGOPT_NATT + OO },
    { "debug-natt",    no_argument, NULL, DBGOPT_NATT + OO },
    { "debug-nat_t",   no_argument, NULL, DBGOPT_NATT + OO },
    { "debug-nat-t",   no_argument, NULL, DBGOPT_NATT + OO },
    { "debug-x509",    no_argument, NULL, DBGOPT_X509 + OO },
    { "debug-dpd",     no_argument, NULL, DBGOPT_DPD + OO },
    { "debug-private", no_argument, NULL, DBGOPT_PRIVATE + OO },

    { "impair-delay-adns-key-answer", no_argument, NULL, DBGOPT_IMPAIR_DELAY_ADNS_KEY_ANSWER + OO },
    { "impair-delay-adns-txt-answer", no_argument, NULL, DBGOPT_IMPAIR_DELAY_ADNS_TXT_ANSWER + OO },
    { "impair-bust-mi2", no_argument, NULL, DBGOPT_IMPAIR_BUST_MI2 + OO },
    { "impair-bust-mr2", no_argument, NULL, DBGOPT_IMPAIR_BUST_MR2 + OO },
    { "impair-sa-fail",    no_argument, NULL, DBGOPT_IMPAIR_SA_CREATION + OO },
    { "impair-die-oninfo", no_argument, NULL, DBGOPT_IMPAIR_DIE_ONINFO  + OO },
    { "impair-jacob-two-two", no_argument, NULL, DBGOPT_IMPAIR_JACOB_TWO_TWO + OO },
    { "impair-major-version-bump", no_argument, NULL, DBGOPT_IMPAIR_MAJOR_VERSION_BUMP + OO },
    { "impair-minor-version-bump", no_argument, NULL, DBGOPT_IMPAIR_MINOR_VERSION_BUMP + OO },
    { "impair-retransmits", no_argument, NULL, DBGOPT_IMPAIR_RETRANSMITS + OO },
    { "impair-send-bogus-isakmp-flag", no_argument, NULL, DBGOPT_IMPAIR_SEND_BOGUS_ISAKMP_FLAG + OO },
    { "whackrecord",     required_argument, NULL, OPT_WHACKRECORD + OO},
    { "whackstoprecord", no_argument, NULL, OPT_WHACKSTOPRECORD + OO},
#endif
#   undef OO
    { 0,0,0,0 }
};

这是whack命令解析时的一个结构信息,可以想象它的配置命令是有多少,而具体实现差不多800行左右的C代码。这里需要注意的是:struct option结构中的val取值通常是单个字符或者数字。由于单字符数量有限,参数比较少时可以使用字符,但是如果像上面辣么多参数,通常采用枚举类型,方便扩展。

四、getopt_long_only函数

getopt_long_only 函数与 getopt_long 函数使用相同的参数表,在功能上基本一致,只是 getopt_long 只将 --name 当作长参数,但 getopt_long_only 会将 --name 和 -name 两种选项都当作长参数来匹配。getopt_long_only 如果选项 -name 不能在 longopts 中匹配,但能匹配一个短选项,它就会解析为短选项。