web-dev-qa-db-ja.com

getoptがオプションの欠落している引数を検出できない

さまざまなコマンドライン引数を取るプログラムがあります。簡単にするために、_-a_、_-b_、_-c_の3つのフラグが必要で、次のコードを使用して引数を解析するとします。

_    int c;
    while((c =  getopt(argc, argv, ":a:b:c")) != EOF)
    {
        switch (c)
        {
             case 'a':
                 cout << optarg << endl;
                 break;
             case 'b':
                 cout << optarg << endl;
                 break;
             case ':':
                 cerr << "Missing option." << endl;
                 exit(1);
                 break;
        }
    }
_

注:aとbはフラグの後にパラメーターを取ります。

しかし、プログラムを呼び出すと問題が発生します

_./myprog -a -b parameterForB
_

ここで、parameterForAを忘れた場合、parameterForA(optargで表される)は_-b_として返され、parameterForBはパラメーターのないオプションと見なされ、optvはargvのparameterForBのインデックスに設定されます。

この状況での望ましい動作は、_':'_の引数が見つからなかった後に_-a_が返され、_Missing option._が標準エラーに出力されることです。ただし、これは_-a_がプログラムに渡される最後のパラメーターである場合にのみ発生します。

問題は、おそらく、[getopt()が_-_で始まるオプションがないと仮定する方法はありますか?

17
finiteloop

getoptについては POSIX標準定義 を参照してください。それは言う

[getopt]がオプション引数の欠落を検出した場合、optstringの最初の文字がコロンの場合はコロン文字( ':')を返し、それ以外の場合は疑問符文字( '?')を返します。

その検出に関しては、

  1. オプションがargvの要素が指す文字列の最後の文字である場合、optargはargvの次の要素を含み、optindは2ずつ増加します。結果のoptindの値がargcより大きい場合、これはオプション引数がない場合、getopt()はエラーを返します。
  2. それ以外の場合、optargはargvのその要素のオプション文字に続く文字列を指し、optindは1ずつ増加します。

getoptは意図したとおりに実行されないように定義されているため、自分でチェックを実装する必要があります。幸いにも、*optargおよびoptindを自分で変更します。

int c, prev_ind;
while(prev_ind = optind, (c =  getopt(argc, argv, ":a:b:c")) != EOF)
{
    if ( optind == prev_ind + 2 && *optarg == '-' ) {
        c = ':';
        -- optind;
    }
    switch ( …
11
Potatoswatter

C++で作業している場合、boost :: program_optionはコマンドライン引数を解析するための私の推奨事項です。

7
Phong

完全な開示:私はこの問題の専門家ではありません。

Gnu.orgの この例 は役に立ちますか? 「?」を扱うようです期待される引数が提供されなかった場合の文字:

while ((c = getopt (argc, argv, "abc:")) != -1)
    switch (c)
    {
       case 'a':
         aflag = 1;
         break;
       case 'b':
         bflag = 1;
         break;
       case 'c':
         cvalue = optarg;
         break;
       case '?':
         if (optopt == 'c')
           fprintf (stderr, "Option -%c requires an argument.\n", optopt);
         else if (isprint (optopt))
           fprintf (stderr, "Unknown option `-%c'.\n", optopt);
         else
           fprintf (stderr,
                    "Unknown option character `\\x%x'.\n",
                    optopt);
         return 1;
       default:
         abort ();
    }

update:おそらく以下は修正として機能しますか?

while((c =  getopt(argc, argv, ":a:b:c")) != EOF)
{
    if (optarg[0] == '-')
    {
        c = ':';
    }
    switch (c)
    {
        ...
    }
}
4
e.James

Boostフリープロジェクトの代替として、getoptの単純なヘッダーのみのC++ラッパーがあります(BSD 3-Clauseライセンスの下で): https://github.com/songgao/flags .hh

リポジトリのexample.ccから取得:

#include "Flags.hh"

#include <cstdint>
#include <iostream>

int main(int argc, char ** argv) {
  uint64_t var1;
  uint32_t var2;
  int32_t var3;
  std::string str;
  bool b, help;

  Flags flags;

  flags.Var(var1, 'a', "var1", uint64_t(64), "This is var1!");
  flags.Var(var2, 'b', "var2", uint32_t(32), "var2 haahahahaha...");
  flags.Var(var3, 'c', "var3", int32_t(42), "var3 is signed!", "Group 1");
  flags.Var(str, 's', "str", std::string("Hello!"), "This is a string, and the description is too long to fit in one line and has to be wrapped blah blah blah blah...", "Group 1");
  flags.Bool(b, 'd', "bool", "this is a bool variable", "Group 2");

  flags.Bool(help, 'h', "help", "show this help and exit", "Group 3");

  if (!flags.Parse(argc, argv)) {
    flags.PrintHelp(argv[0]);
    return 1;
  } else if (help) {
    flags.PrintHelp(argv[0]);
    return 0;
  }

  std::cout << "var1: " << var1 << std::endl;
  std::cout << "var2: " << var2 << std::endl;
  std::cout << "var3: " << var3 << std::endl;
  std::cout << "str:  " << str << std::endl;
  std::cout << "b:    " << (b ? "set" : "unset") << std::endl;

  return 0;
}
2
Song Gao

getoptには非常に多くの異なるバージョンがあるため、1つのバージョンで機能させることができたとしても、回避策が壊れる可能性のあるバージョンが少なくとも5つはあります。 getoptを使用する圧倒的な理由がない限り、 Boost.Program_options など、他のことを検討します。

1
Jerry Coffin