Linux下C程序命令行参数处理

时间:2022-05-31 04:13:19
       Linux下开发C程序,甚至是GUI程序,都可能需要处理复杂的命令行参数。健全、可靠的复杂命令行参数处理机制,可使程序方便使用,也更显专业。Linux下几乎所有的命令都提供了参数处理机制,包括短选项和长选项。

   POSIX标准中对程序名、参数作了如下相关约定:
     * 程序名不宜少于2个字符且不多于9个字符;
    * 程序名应只包含小写字母和阿拉伯数字;
    * 选项名应该是单字符活单数字,且以短横‘-‘为前綴;
    * 多个不需要选项参数的选项,可以合并。(譬如:foo -a -b -c ---->foo -abc)
    * 选项与其参数之间用空白符隔开;
    * 选项参数不可选。
    * 若选项参数有多值,要将其并为一个字串传进来。譬如:myprog -u "arnold,joe,jane"。这种情况下,需要自己解决这些参数的分离问题。
    * 选项应该在操作数出现之前出现。
    * 特殊参数‘--'指明所有参数都结束了,其后任何参数都认为是操作数。
    * 选项如何排列没有什么关系,但对互相排斥的选项,如果一个选项的操作结果覆盖其他选项的操作结果时,最后一个选项起作用;如果选项重复,则顺序处理。
    * 允许操作数的顺序影响程序行为,但需要作文档说明。
    * 读写指定文件的程序应该将单个参数'-'作为有意义的标准输入或输出来对待。

  GNU鼓励程序员使用--help、--verbose等形式的长选项。这些选项不仅不与POSIX约定冲突,而且容易记忆,另外也提供了在所有GNU工具之间保持一致性的机会。 GNU长选项有自己的约定
     * 对于已经遵循POSIX约定的GNU程序,每个短选项都有一个对应的长选项。
    * 额外针对GNU的长选项不需要对应的短选项,仅仅推荐要有。
    * 长选项可以缩写成保持惟一性的最短的字串。
    * 选项参数与长选项之间或通过空白字符活通过一个'='来分隔。
    * 选项参数是可选的(只对短选项有效)。
    * 长选项允许以一个短横线为前缀。

  C程序通过argc和argv参数访问它的命令行参数,通过main()函数调用和处理:int main(int argc, char *argv[])。一般情况下,我们事先约定好参数的顺序位置,然后在main函数中进行简单处理。这种方式实现比较简单,然后用户使用起来很不方便,Linux下的各种工具的命令行参数可以是不分先后次序的。幸运的是,Linux为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;

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


          getopt() 函数是一个标准库调用,可允许使用直接的 while/switch 语句方便地逐个处理命令行参数和检测选项(带或不带附加的参数)。getopt_long() 允许在几乎不进行额外工作的情况下处理更具描述性的长选项,非常受开发人员的欢迎。下面用笔者开发的一个Daemon程序wsiod中的参数处理来说明具体的处理过程。wsiod参数帮助信息如下:

[liuag] /home/liuag/workspace/WSIO/WSIO-1.3/server > ./wsiod --help
Usage:  wsiod [OPTION]

WSIO server based on web service.
Mandatory arguments to long options are mandatory for short options too.
        -h, --host           hostname in soap_bind, default is host which the service runs
        -p, --port           port which the sercer runs on, default is 8080
        -b, --backlog        request backlog, default is 100
        -t, --type           server type, default is COMMON
        -k, --keepalive      attempt to keep socket connections alive
        -c, --chunk          use HTTP chunking
        -d, --dime           use DIME encoding
        -D, --debug          print debug info
            --help           print this help

Server type:
        COMMON               the simplest server
        STANDALONE           stand-alone server, which can run on port 80
        MULTITHREAD          multi-thread stand-alone server
        POOL                 using a pool of servers
        QUEUE                using a queue of requests for server
        GSI                  prethreaded server with GSI enabled

Report bugs to <Aigui.LIU@ihep.ac.cn>.

wsiod参数处理实现C程序段如下:

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

enum SERVERTYPE{COMMON, STANDALONE, MULTITHREAD, POOL, QUEUE, GSI};
int keepalive = 0, dime = 0, chunk = 0;

/* WSIO server main function */
int main(int argc, char **argv)
{
  int c;
  char host[128] = "localhost";
  char log_buf[LOGBUFSZ];
  int port = 8080, backlog = 100;
  enum SERVERTYPE servertype  = COMMON;
  int helpflg = 0,
      errflg = 0,
      debug = 0;
  struct option longopts[] =
  {
    {"host", 1, 0, 'h'},
    {"port", 1, 0, 'p'},
    {"backlog", 1, 0, 'b'},
    {"type", 1, 0, 't'},
    {"keepalive", 0, 0, 'k'},
    {"chunk", 0, 0, 'c'},
    {"dime", 0, 0, 'd'},
    {"debug", 0, 0, 'D'},
    {"help", 0, &helpflg, 1},
    {0, 0, 0, 0}
  };

  while ((c = getopt_long (argc, argv, "h:p:b:t:kcdD", longopts, NULL)) != EOF)
  {
    switch(c)
    {
      case 'h':
        sprintf(host, "%s", optarg);
        break;
      case 'p':
        port = atoi(optarg);
        break;
      case 'b':
        backlog = atoi(optarg);
        break;
      case 't':
        switch(*optarg)
    {
      case 'C':
        servertype = COMMON;
        break;
      case 'S':
        servertype = STANDALONE;
        break;
      case 'M':
        servertype = MULTITHREAD;
        break;
      case 'P':
        servertype = POOL;
        break;
      case 'Q':
        servertype = QUEUE;
        break;
      case 'G':
        servertype = GSI;
      default:
        break;
    }
        break;
      case 'k':
          keepalive = 1;
    break;
      case 'c':
          chunk = 1;
    break;
      case 'd':
          dime = 1;
    break;
      case 'D':
          debug = 1;
    break;
      case '?':
        errflg++;
        break;
      default:
        break;
    }
  }

  if(helpflg || errflg)
  {
     fprintf(stderr,"Usage:    wsiod [OPTION]/n/n%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
         "WSIO server based on web service./n",
    "Mandatory arguments to long options are mandatory for short options too./n",
    "/t-h, --host           hostname in soap_bind, default is host which the service runs/n ",
    "/t-p, --port           port which the sercer runs on, default is 8080/n",
    "/t-b, --backlog        request backlog, default is 100/n",
    "/t-t, --type           server type, default is COMMON/n",
    "/t-k, --keepalive      attempt to keep socket connections alive/n",
        "/t-c, --chunk          use HTTP chunking/n",
        "/t-d, --dime           use DIME encoding/n",
        "/t-D, --debug          print debug info/n",
    "/t    --help           print this help/n/n",
    "Server type:/n",
    "/tCOMMON               the simplest server/n",
    "/tSTANDALONE           stand-alone server, which can run on port 80/n"
    "/tMULTITHREAD          multi-thread stand-alone server/n",
    "/tPOOL                 using a pool of servers/n",
    "/tQUEUE                using a queue of requests for server/n",
    "/tGSI                  prethreaded server with GSI enabled/n/n",
    "Report bugs to <Aigui.LIU@ihep.ac.cn>./n"
     );
    exit(0);
  }

  /* 省略部分 */

  return 0;
}