【问题标题】:getopt with repeated and optional arguments带有重复和可选参数的 getopt
【发布时间】:2015-12-09 20:12:38
【问题描述】:

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

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

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

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

  1. 将所有大小存储在一个数组中,然后将它们写入表中。
  2. 解析时直接写入大小

对于第一个选择,困难在于我不知道分区的数量。但是我仍然可以通过 argc 或更好地通过 (argc-1)/2 来对这个数字进行专业化,并且它会起作用。

第二个选择我不知道要写哪个文件。

那么,获得所有这些参数的最佳选择是什么?如何获得这个可选名称?

【问题讨论】:

    标签: c parsing getopt getopt-long


    【解决方案1】:

    getopt 可以很好地处理重复和可选参数。对于重复的参数,getopt 的每次调用都会给你下一个参数。 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
    

    【讨论】:

    • 谢谢,但实际上困难在于在写入可选/默认文件之前获取所有尺寸,所以它是关于存储每个 -s 的 optarg。
    • @user3593232 我没有得到你。上面的代码将允许您解析所有大小。文件写入代码将在那之后进行。当然上面的代码只是一个示例,只打印出尺寸。您的真实代码会将解析后的大小保存到数组、列表或其他结构中。也就是说,将上面的printf("size=%d\n", atoi(optarg)); 行替换为存储相同optarg 值的代码。
    • “问题”是我不知道数组的大小。但这不是一个真正的问题,因为我可以用 argc 主修它......我承认这不是一个好问题。起初让我感到不安的是,我将声明一个不会被实现的数组。在我的情况下,这没什么大不了的,但是如果有很多可能的不同重复选项,那么为每个选项声明一个 argc 数组可能会造成很大损失。我可能有点偏执!
    【解决方案2】:

    我认为你想多了。我知道尝试避免使用 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 中嵌套 switch 的常用方法。在switch中,continue会继续for循环,而break会跳出switch;因为所有不会终止 for 循环 continue 的 switch 操作,所以 switch 块后面的任何内容都只针对显式 breaks 的 switch 操作执行。因此,switch 块之后的 break 恰好在 switch 操作执行 break 的情况下打破了 for 循环。

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

    【讨论】:

      猜你喜欢
      • 2016-11-15
      • 1970-01-01
      • 2013-11-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-03-27
      • 2014-03-22
      • 2014-07-04
      相关资源
      最近更新 更多