さまざまなコマンドライン引数を取るプログラムがあります。簡単にするために、_-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()
が_-
_で始まるオプションがないと仮定する方法はありますか?
getopt
については POSIX標準定義 を参照してください。それは言う
[getopt]がオプション引数の欠落を検出した場合、optstringの最初の文字がコロンの場合はコロン文字( ':')を返し、それ以外の場合は疑問符文字( '?')を返します。
その検出に関しては、
- オプションがargvの要素が指す文字列の最後の文字である場合、optargはargvの次の要素を含み、optindは2ずつ増加します。結果のoptindの値がargcより大きい場合、これはオプション引数がない場合、getopt()はエラーを返します。
- それ以外の場合、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 ( …
C++で作業している場合、boost :: program_optionはコマンドライン引数を解析するための私の推奨事項です。
完全な開示:私はこの問題の専門家ではありません。
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)
{
...
}
}
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;
}
getopt
には非常に多くの異なるバージョンがあるため、1つのバージョンで機能させることができたとしても、回避策が壊れる可能性のあるバージョンが少なくとも5つはあります。 getoptを使用する圧倒的な理由がない限り、 Boost.Program_options など、他のことを検討します。