带有重复和可选参数的getopt

时间:2015-12-09 20:12:39

标签: c parsing getopt getopt-long

对于文件中文件系统的简单C项目,我必须创建一个用于编写分区表的命令。它只包含分区数和大小,非常简单。

它应该像mk_part -s size [-s size ...] [name]一样工作。

[name]是磁盘的文件名,它是可选的,因为提供了默认的文件。

我不太了解getopt_long(和getopt),但我读到的只是我在一段时间内获得选项,所以我的两种处理方式是:

  1. 将所有大小存储在数组中,然后将其写入表中。
  2. 在解析期间直接写入大小
  3. 对于第一个选择,困难在于我不知道分区的数量。但我仍然可以通过argc或更好的(argc-1)/ 2对这个数字进行处理,这样就可以了。

    对于第二种选择,我不知道要写哪个文件。

    那么获取所有这些参数的最佳替代方法是什么?如何获得此选项名称?

2 个答案:

答案 0 :(得分:2)

getopt可以很好地处理重复和可选的args。对于重复的args,每次getopt的调用都会给你下一个arg。 getopt并不关心它是否重复。对于最后的arg,只需要在解析所有选项后检查它的存在。下面是从getopt手册页中的示例修改的代码,用于处理您的场景:

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

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

    while ((opt = getopt(argc, argv, "s:")) != -1) {
        switch (opt) {
        case 's':
            printf("size=%d\n", atoi(optarg));
            break;
        default: /* '?' */
            exit(EXIT_FAILURE);
        }
    }

    if (optind < argc) {
        printf("name=%s\n", argv[optind]);
    } else {
        printf("optional name arg not present\n");
    }

    exit(EXIT_SUCCESS);
}

以下是程序的一些示例运行,显示它处理重复的选项和最后的arg。

$ ./a.out -s 10 -s 20 -s 30
size=10
size=20
size=30
optional name arg not present

$ ./a.out -s 1 my_name
size=1
name=my_name

答案 1 :(得分:0)

我认为你是在思考这个问题。我知道尝试避免使用malloc总是很诱人,但选项解析的效率永远不会(*)很重要。您只需解析一次选项,并以初始化新进程,查找可执行文件,链接和加载它以及启动命令的所有其余过程为代价,解析选项所需的时间可能不是噪音

所以尽可能以最简单的方式做到这一点。这是一个可能的大纲:

int main(int argc, char* argv) {
  /* These variables describe the options */
  int  nparts = 0;              // Number of partitions
  unsigned long* parts = NULL;  // Array of partitions (of size nparts)
  const char* diskname="/the/default/name"; // Disk's filename

  for (;;) {
    switch (getopt(argc, argv, "s:")) {
      case '?':
        /* Print usage message */
        exit(1);
      case 's':
        /* Some error checking missing */
        parts = realloc(parts, ++nparts * sizeof *parts);
        parts[nparts - 1] = strtoul(optarg, NULL, 0);
        continue;
      case -1:
        break;
    }
    break;
  }
  if (optind < argc) diskname = argv[optind++];
  if (optind != argc) {
    /* print error message */ 
    exit(1);
  }
  return do_partitions(diskname, parts, nparts);
}

上面的代码缺少很多错误检查和其他细节,但它很简短。它只是在每次找到新大小时重新分配分区数组。 (这可能不像你想象的那么糟糕,因为realloc本身可能足够聪明,能够以指数方式增加分配大小。但即使它很糟糕,它也不会经常发生,甚至不会注意到。)

使用continuebreak的技巧是将开关嵌套在for中的常用方法。在切换中,continue将继续for循环,而break将突破切换;因为所有不终止for循环continue的开关动作,所以切换块后面的任何内容仅针对明确break s的切换动作执行。因此,break块之后的switch会在切换操作执行break的情况下中断for循环。

在调用执行重新分区的函数之前,您可能需要检查是否至少定义了一个分区大小。