コマンドライン引数を処理するための設計パターンは何ですか
-
09-06-2019 - |
質問
コマンド ラインから実行可能なプログラムを作成している場合、場合によっては複数の引数とともに、いくつかのオプションやフラグをユーザーに提供したいことがよくあります。私はこれで何度もつまずきましたが、引数をループして適切なハンドラー関数を呼び出すための何らかの設計パターンはあるのでしょうか?
考慮する:
myprogram -f filename -d directory -r regex
言語の組み込み関数を使用して引数を取得した後、ハンドラー関数をどのように編成しますか?(回答を明確にするのに役立つ場合は、言語固有の回答を歓迎します)
解決
文書化された処理の「パターン」を私は知りません。
引数を処理するための最も古いライブラリ/API の 1 つは getopt だと思います。「getopt」でグーグル検索すると、たくさんのマニュアル ページと実装へのリンクが表示されます。
通常、アプリケーションには、引数プロセッサが通信方法を知っている環境設定または設定サービスがあります。引数は、このサービス内でアプリケーションがクエリするものに変換されます。これは、設定の辞書 (「ファイル名」という名前の文字列設定など) と同じくらい単純なものにすることができます。
他のヒント
次の答えがあなたが探しているものにより近いと思います。
テンプレート パターンの適用を検討する必要があります (「デザイン パターン」[Gamma など] のテンプレート メソッド)。
簡単に言うと、全体的な処理は次のようになります。
If the arguments to the program are valid then
Do necessary pre-processing
For every line in the input
Do necessary input processing
Do necessary post-processing
Otherwise
Show the user a friendly usage message
つまり、次のメソッドを持つ ConsoleEngineBase クラスを実装します。
PreProcess()
ProcessLine()
PostProcess()
Usage()
Main()
次に、ConsoleEngine() インスタンスをインスタンス化し、それを開始する Main() メッセージを送信するシャーシを作成します。
これをコンソールまたはコマンド ライン プログラムに適用する方法の良い例を確認するには、次のリンクを確認してください。http://msdn.microsoft.com/en-us/magazine/cc164014.aspx
この例は C# で示されていますが、アイデアは他の環境でも簡単に実装できます。
GetOpt() は、引数処理 (前処理) に該当する部分に過ぎないと考えることになります。
お役に立てれば。
言語については言及しませんでしたが、私たちは Java を気に入っています Apache Commons CLI. 。C/C++ の場合は、getopt。
これに関していくつかコメントを…
まず、それ自体にはパターンはありませんが、文法が与えられればパーサーは簡単に生成できるため、パーサーの作成は本質的に機械的な作業になります。Bison や ANTLR などのツールが思い浮かびます。
とはいえ、パーサー ジェネレーターは通常、コマンド ラインには過剰です。したがって、一般的なパターンは、退屈な詳細を扱うのに飽きて、それを実行してくれるライブラリを見つけるまで、(他の人が実証したように)自分で何度か作成することです。
私は C++ 用に getopt によって与えられる多くの労力を節約し、テンプレートをうまく利用できるものを書きました。 TCLAP
そうですね、古い投稿ですが、引き続き貢献したいと思います。質問はデザインパターンの選択を目的としていましたが、どのライブラリを使用するかについて多くの議論が見られました。使用するテンプレートのデザインパターンについて説明しているlindsayによるMicrosoftリンクをチェックしました。
しかし、私はその投稿に納得がいきません。テンプレート パターンの目的は、他のさまざまなクラスによって実装され、均一な動作を持つテンプレートを定義することです。コマンドラインの解析はこれに当てはまらないと思います。
私はむしろ「Command」デザインパターンを使いたいと思っています。このパターンは、メニュー駆動のオプションに最適です。
http://www.blackwasp.co.uk/Command.aspx
したがって、あなたの場合、-f、-d、および-rはすべて、共通または別のレシーバーが定義されたコマンドになります。そうすることで、将来さらに多くの受信者を定義できるようになります。次のステップでは、処理チェーンが必要な場合に備えて、これらのコマンドの責任をチェーンします。私ならどちらを選びますか。
http://www.blackwasp.co.uk/ChainOfResponsibility.aspx
コマンドライン処理やメニュー駆動のアプローチのコードを整理するには、これら 2 つの組み合わせが最適だと思います。
の ブースト::プログラムオプション C++ を使用していて、Boost を使用する余裕がある場合は、ライブラリが便利です。
フラグを使用して設定する「config」オブジェクトと、コマンド ラインの解析を担当し、オプションの定数ストリームを提供する適切なコマンド ライン パーサーがあると仮定すると、ここに疑似コードのブロックが入ります。
while (current_argument = cli_parser_next()) {
switch(current_argument) {
case "f": //Parser strips the dashes
case "force":
config->force = true;
break;
case "d":
case "delete":
config->delete = true;
break;
//So on and so forth
default:
printUsage();
exit;
}
}
私は「-t text」や「-i 44」のようなオプションを好みます。私は「-fname」や「--very-long-argument=some_value」は好きではありません。
「-?」、「-h」、および「/h」はすべてヘルプ画面を生成します。
私のコードは次のようになります。
int main (int argc, char *argv[])
{ int i;
char *Arg;
int ParamX, ParamY;
char *Text, *Primary;
// Initialize...
ParamX = 1;
ParamY = 0;
Text = NULL;
Primary = NULL;
// For each argument...
for (i = 0; i < argc; i++)
{
// Get the next argument and see what it is
Arg = argv[i];
switch (Arg[0])
{
case '-':
case '/':
// It's an argument; which one?
switch (Arg[1])
{
case '?':
case 'h':
case 'H':
// A cry for help
printf ("Usage: whatever...\n\n");
return (0);
break;
case 't':
case 'T':
// Param T requires a value; is it there?
i++;
if (i >= argc)
{
printf ("Error: missing value after '%s'.\n\n", Arg);
return (1);
}
// Just remember this
Text = Arg;
break;
case 'x':
case 'X':
// Param X requires a value; is it there?
i++;
if (i >= argc)
{
printf ("Error: missing value after '%s'.\n\n", Arg);
return (1);
}
// The value is there; get it and convert it to an int (1..10)
Arg = argv[i];
ParamX = atoi (Arg);
if ((ParamX == 0) || (ParamX > 10))
{
printf ("Error: invalid value for '%s'; must be between 1 and 10.\n\n", Arg);
return (1);
}
break;
case 'y':
case 'Y':
// Param Y doesn't expect a value after it
ParamY = 1;
break;
default:
// Unexpected argument
printf ("Error: unexpected parameter '%s'; type 'command -?' for help.\n\n", Arg);
return (1);
break;
}
break;
default:
// It's not a switch that begins with '-' or '/', so it's the primary option
Primary = Arg;
break;
}
}
// Done
return (0);
}
mes5k による ANTLR の回答を参考にしています。これ コードプロジェクトへのリンク この記事では、ANLTR と、アプリに実行してもらいたいアクションを実装するための訪問パターンの使用について説明します。よく書かれており、検討する価値があります。
コマンドラインプロセッサライブラリを使用することをお勧めします。 ロシア人の男 きちんとしたものを作成しましたが、世の中にはたくさんあります。時間を節約できるので、コマンド ライン スイッチを解析するのではなく、アプリの目的に集中できます。
Getopt が唯一の方法です。
通訳パターンはどうでしょうか?http://www.dofactory.com/net/interpreter-design-pattern
このための言語については言及していませんが、getopt を中心とした非常に優れた Objective-C ラッパーを探している場合は、Dave Dribin の DDCLI フレームワークが非常に優れています。
私が使用するのは、 Getopts::std そして Getopts::long Perlでも、 ゲトップト Cの関数。これにより、パラメータの解析と形式が標準化されます。他の言語には、これらを処理するための異なるメカニズムがあります。
お役に立てれば
通常、標準設計は getopt の動作に従い、.NET、Python、C、Perl、PHP など、多くの言語用の getopt ライブラリがあります。
基本的な設計は、ループ内でチェックするために渡された引数を部分ごとに返すコマンド ライン パーサーを備えていることです。
これ この記事ではさらに詳しく説明しています。
私は図書館にはあまり興味がありませんが、確かに役に立ちます。私は、例として、平均的なフラグの束と長い引数の束の処理を示す「疑似コード」を探していました。