【问题标题】:Getopt returning -1 for certain order of optionsGetopt 为特定顺序的选项返回 -1
【发布时间】:2013-11-03 22:37:08
【问题描述】:

当我在其他选项之前有一个非选项时,基本上 getopt 会返回 -1。

    n = atoi(argv[1]);

    while ((opt = getopt(argc, argv, "hi:o:")) != -1) {
        switch (opt) {
        case 'i':
            ifile = optarg; 
            break;
        case 'o':
            ofile = optarg; 
            break;
        case 'h':
            printf("...");
            break;
        default:
            printf("Invalid Option\n");
            exit(0);
        }
    }

示例:

./a.out 22 -i infile -o outfile

这使得 getopt 返回 -1 并且 switch 语句永远不会被执行。

./a.out -i infile -o outfile

但是这可行,但我真的希望第一个选项只是一个数字,而不必包含另一个选项。

我认为 getopt 应该处理这种情况,但我可能错了。任何帮助表示赞赏!

【问题讨论】:

  • 这就是它应该工作的方式。选项在遇到第一个非选项时结束——从那时起的所有内容都应该是文件名。
  • 处理乱序参数是 GNU 扩展,而不是 Posix。确保不要在任何地方强制执行“严格 Posix”模式。
  • 你为什么不做a.out -i infile -o outfile 22
  • @Barmar 很有趣,我不知道它应该如何工作。我想我会接受你的建议。

标签: c linux unix getopt


【解决方案1】:

通用解决方案

解决问题的最简单方法是:

n = atoi(argv[1]);

argv[1] = argv[0];
++argv;
--argc;

while ((opt = getopt(argc, argv, "hi:o:")) != -1)
{
    ...as before...
}

这有效地消耗了第一个选项,然后将事情重新调整为正统格式。这适用于远程符合标准(事实上和法律上)的任何版本的getopt()。请注意,如果您不重新分配 argv[1] = argv[0];getopt() 将使用原始 argv[1] 中的值(本例中的数字)作为“程序名称”。

使用 GNU getopt()

使用 GNU getopt(),有两种方法可以解决这个问题。一个使用“按顺序返回”功能,以便非选项(“文件名”)参数被视为以选项字符代码 1(ASCII SOH 控制字符;又名 Control-A)。另一个使用默认模式,getopt() 在处理选项时对其进行置换。请注意,默认情况下,这在 Mac OS X 和其他平台上不可用。

这段代码几乎同时完成了这两项工作;您只需使用 "-hi:o:" 在“置换”模式(如图所示)和“按顺序返回”模式之间更改选项字符串。

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

int main(int argc, char **argv)
{
    int n;
    int opt;
    char *ifile = 0;
    char *ofile = 0;

    n = atoi(argv[1]);

    while ((opt = getopt(argc, argv, "hi:o:")) != -1)
    {
        printf("Option: %c\n", opt);
        switch (opt)
        {
        case 'i':
            ifile = optarg;
            break;
        case 'o':
            ofile = optarg;
            break;
        case 'h':
            printf("...");
            break;
        case 1:
            n = atoi(optarg);
            break;
        default:
            printf("Invalid Option %d\n", opt);
            exit(1);
        }
    }

    printf("argc = %d; optind = %d\n", argc, optind);
    for (int i = optind; i < argc; i++)
        printf("argv[%d] = %s\n", i, argv[i]);

    if (ifile != 0)
        printf("i-file: %s\n", ifile);
    if (ofile != 0)
        printf("o-file: %s\n", ofile);
    printf("%d\n", n);

    return 0;
}

使用“permute”运行示例:

$ go9 9 -i in -o out
Option: i
Option: o
argc = 6; optind = 5
argv[5] = 9
i-file: in
o-file: out
9
$

使用“按顺序返回”的示例运行:

$ go9 9 -i in -o out
Option: 
Option: i
Option: o
argc = 6; optind = 6
i-file: in
o-file: out
9
$

如果您希望它工作,请确保您没有设置环境变量 POSIXLY_CORRECT。您可以在选项字符串前加上 + 以强制执行类似 POSIX 的行为。

【讨论】:

  • 不是更简单的通用解决方案是:optind = 2;? Afaics 是标准的 posix。 (或者更好,if (argc &gt; 1) optind = 2;
  • 没有记录如果你设置optind会发生什么。如果你愿意,你可以玩火;我宁愿确保参数列表是正常格式。
  • 如果您将 optind 设置为 0,则它专门为 UB,如果您将其设置为超出范围,则为隐式 UB。但任何其他用途都很明确:“变量 optind 是要处理的 argv[] 向量的下一个元素的索引。”这不是魔法。
  • 假设你有-abc作为参数和选项字符串"ab:";该函数知道如何返回a,然后返回b,其中optarg 指向"c";外部接口中没有任何内容记录它如何知道从中断的地方继续。基本上,这取决于你。我不会冒险;欢迎你挖自己的坟墓。你可能会侥幸逃脱,但是……
  • 我已经摆脱了它超过三个十年,所以它对我来说足够安全。当然,我的坟墓越来越近了,但我想我还有一些时间:)
猜你喜欢
  • 2014-04-13
  • 2013-01-02
  • 1970-01-01
  • 2021-08-16
  • 1970-01-01
  • 2021-10-10
  • 1970-01-01
  • 1970-01-01
  • 2019-01-07
相关资源
最近更新 更多