C#でコマンドライン引数を解析する最良の方法?[閉まっている]


731

パラメータを取るコンソールアプリケーションを構築する場合、に渡される引数を使用できますMain(string[] args)

以前は、その配列にインデックスを付けたりループさせたりして、値を抽出するためにいくつかの正規表現を実行しました。ただし、コマンドがさらに複雑になると、解析がかなり醜くなります。

だから私は興味があります:

  • 使用するライブラリ
  • 使用するパターン

コマンドは常に、ここで回答されているような一般的な標準に準拠していると想定します。


以前のディスカッションであるsplit-string-taining-command-line-parameters-into-string-in-c#には、いくつかの答えがあるかもしれません。
ギメル2009年

1
こんにちは、申し訳ありませんが、少し話題から外れています。ただし、「アプリケーション設定」を使用して引数をアプリケーションに渡します。私はそれが非常に使いやすく、引数/ファイル解析を書く必要がなく、追加のライブラリも必要ないことに気付きました。 msdn.microsoft.com/en-us/library/aa730869(VS.80).aspx
Steve Steveを

44
@call me Steve:コマンドライン引数のポイントは、呼び出しごとに異なる可能性があるということです-アプリケーション設定でそれをどのように行うのですか?
reinierpost 2010

回答:


324

NDesk.OptionsDocumentation)やMono.Options(同じAPI、異なる名前空間)を使用することを強くお勧めします。ドキュメント

bool show_help = false;
List<string> names = new List<string> ();
int repeat = 1;

var p = new OptionSet () {
    { "n|name=", "the {NAME} of someone to greet.",
       v => names.Add (v) },
    { "r|repeat=", 
       "the number of {TIMES} to repeat the greeting.\n" + 
          "this must be an integer.",
        (int v) => repeat = v },
    { "v", "increase debug message verbosity",
       v => { if (v != null) ++verbosity; } },
    { "h|help",  "show this message and exit", 
       v => show_help = v != null },
};

List<string> extra;
try {
    extra = p.Parse (args);
}
catch (OptionException e) {
    Console.Write ("greet: ");
    Console.WriteLine (e.Message);
    Console.WriteLine ("Try `greet --help' for more information.");
    return;
}

14
NDesk.optionsは素晴らしいですが、複数の異なるコマンドでコンソールアプリを実際にサポートするようには見えません。それが必要な場合は、NDesk.Optionsに基づいて構築されたManyConsoleを試してください。nuget.org/ List
Frank

5
複数の異なるコマンドを持つ1つのアプリがある場合、OptionSetを「階層化」します。mdoc(docs.go-mono.com/index.aspx?link=man%3amdoc%281%29)を取得します。これには、「グローバル」なOptionSet(github.com/mono/mono/blob/master/mcs/tools/ mdoc /…)コマンドごとのOptionSetに委任します(例:github.com/mono/mono/blob/master/mcs/tools/mdoc/…
jonp

3
NDeskは私のための仕事はありません。整数の引数は読み取れますが、文字列は読み取れません。変数は、引数の値(例: 'serverName'、 'ApplicationName')の代わりに引数(例: 's'、 'a'など)を取得し続けます。あきらめて、代わりに「コマンドラインパーサーライブラリ」を使用しました。今のところOK。
ジェイ

2
@AshleyHenderson一つには、それは小さくて柔軟です。ほとんどのソリューションは、オプションの名前付き引数でのみ機能します(つまり、のように行うことはできませんgit checkout master)。または、それらの引数は柔軟ではありません(つまり、--foo 123= --foo=123= -f 123= -f=123および-v -h= もサポートしていません-vh)。
平日

1
@FrankSchwieterman、それは独自の答えになるはずです。そしてヒントをありがとう、ManyConsoleは本当のごちそうであり、私にぴったりです。
quentin-starin 2013

197

コマンドラインパーサーライブラリ(http://commandline.codeplex.com/)が本当に好きです。属性を介してパラメータを設定する非常にシンプルでエレガントな方法があります。

class Options
{
    [Option("i", "input", Required = true, HelpText = "Input file to read.")]
    public string InputFile { get; set; }

    [Option(null, "length", HelpText = "The maximum number of bytes to process.")]
    public int MaximumLenght { get; set; }

    [Option("v", null, HelpText = "Print details during execution.")]
    public bool Verbose { get; set; }

    [HelpOption(HelpText = "Display this help screen.")]
    public string GetUsage()
    {
        var usage = new StringBuilder();
        usage.AppendLine("Quickstart Application 1.0");
        usage.AppendLine("Read user manual for usage instructions...");
        return usage.ToString();
    }
}

6
これも私が定住した図書館です。私は長年維持する必要がある大企業向けのアプリケーションを書いています-このライブラリは2005年から継続的に更新されており、人気があるようで、C#コミュニティでアクティブな人々によって書かれており、BSDスタイルでライセンスされていますサポートは消えます。
Charles Burns

これもオススメです。私の唯一の問題はだった:指定はarguement組み合わせ(例えば持っていた移動arguementは、ソースとdest引数を持たなければならない場合は)許され属性をどうすることも可能。しかし、別のArgument検証ロジックでそれを行う方がよいかもしれません
Lyndon White

1
Optionsクラスが好きです。名前のないパラメータやフラグ--recursiveもサポートしているようです。
2012年

2
私はそれをテストしたばかりで、わずか数分でアプリケーションにオプションを実装しました。ライブラリを使用するのは非常に簡単です。
Trismegistos 2012

3
このライブラリは自分にとって非常に制限的であることがわかりました。排他的なセットが必要な場合、各セットに必要なオプションを定義できないため、手動でチェックする必要があります。名前のない値の最小要件を定義することはできません。手動で確認する必要もあります。ヘルプ画面ビルダーもまったく柔軟性がありません。ライブラリの動作がすぐにニーズに合わない場合は、実質的に何も変更することはできません。
Sergey Kostrukov 2013年

50

WPF TestApiライブラリは、 C#の開発のための素敵なコマンドラインパーサの1が付属しています。APIに関するIvo Manolovのブログから、これを調べることを強くお勧めします

// EXAMPLE #2:
// Sample for parsing the following command-line:
// Test.exe /verbose /runId=10
// This sample declares a class in which the strongly-
// typed arguments are populated
public class CommandLineArguments
{
   bool? Verbose { get; set; }
   int? RunId { get; set; }
}

CommandLineArguments a = new CommandLineArguments();
CommandLineParser.ParseArguments(args, a);

19
+1。コマンドラインの解析は、ベンダーのサポートが遠回りになったとしても、サードパーティのツールを介してではなく、実際にベンダー(つまり、Microsoft)からのものである必要があります。
Joel Coehoorn、2010

2
とはいえ、受け入れられた答え(モノ)が次善の策です。
Joel Coehoorn、2010

6
@ジョエル、コマンドラインの解析がベンダーからのものでなければならないという重要な部分は何ですか あなたの理由は何ですか?
greenoldman 2010

3
@marcias:彼はおそらくそれはおそらく箱から出していたはずだったと思います...多くのことのように:)
user7116

ライブラリは巨大です!必要以上に多く含まれています...
Riri

24

2
NDeskオプションには非常に優れたAPIがあります
user35149 '06

2
私はNDeskに別の投票を追加します。それはうまく機能し、邪魔にならないし、十分に文書化されています。
テレンス

1
Mono.GetOptionsは非常に古く、NDesk.Optionsははるかに優れています(または、必要に応じてMono.Optionsを使用します。同じクラスです。ここで、anonsvn.mono-project.com/source/trunk/mcs/class/Mono.Options/
Matt Enright、2010年

7
@Adam Oren:私の答えは1年と1か月前です!モノトランクの構造がリファクタリングされました。そのコードはanonsvn.mono-project.com/viewvc/branches/mono-2-2/mcs/class/…に
abatishchev

6
@Tormod:廃止されたのはMono.GetOptionsであり、Mono.Optionsではありません。Mono.Optionsは引き続き維持されます。
jonp

14

誰もが独自のペットコマンドラインパーサーを持っているように見えます。

http://bizark.codeplex.com/

このライブラリには、コマンドラインからの値でクラスを初期化するコマンドラインパーサーが含まれています。それはたくさんの機能を持っています(私は長年にわたってそれを構築してきました)。

ドキュメントから...

BizArkフレームワークのコマンドライン解析には、次の主要な機能があります。

  • 自動初期化:クラスのプロパティは、コマンドライン引数に基づいて自動的に設定されます。
  • デフォルトのプロパティ:プロパティ名を指定せずに値を送信します。
  • 値の変換: BizArkに含まれている強力なConvertExクラスを使用して、値を適切な型に変換します。
  • ブールフラグ:フラグは、引数(例:trueの場合は/ b、falseの場合は/ b-)を使用するか、true / false、yes / noなどの値を追加することで指定できます。
  • 引数の配列:コマンドライン名の後に複数の値を追加するだけで、配列として定義されているプロパティを設定できます。たとえば、/ x 1 2 3は、xに配列{1、2、3}を入力します(xが整数の配列として定義されていると想定)。
  • コマンドラインエイリアス:プロパティは、複数のコマンドラインエイリアスをサポートできます。たとえば、ヘルプはエイリアス?を使用します。
  • 部分的な名前の認識:完全な名前やエイリアスを入力する必要はありません。パーサーが他のプロパティ/エイリアスを明確にするのに十分なだけを入力してください。
  • ClickOnceのサポート:プロパティがClickOnce配置アプリケーションのURLでクエリ文字列として指定されている場合でも、プロパティを初期化できます。コマンドライン初期化メソッドは、ClickOnceとして実行されているかどうかを検出するため、コードを使用するときにコードを変更する必要はありません。
  • /を自動的に作成しますか?ヘルプ:これには、コンソールの幅を考慮した適切なフォーマットが含まれます。
  • ファイルへのコマンドライン引数のロード/保存:これは、複数回実行するコマンドライン引数の大規模で複雑なセットが複数ある場合に特に役立ちます。

2
BizArkのコマンドラインパーサーは、他のものよりもはるかに簡単で流暢です。強くお勧めします!
ボリスモディレフスキー


9

CLAP(コマンドライン引数パーサー)には使用可能なAPIがあり、文書化されています。パラメータを注釈するメソッドを作成します。 https://github.com/adrianaisemberg/CLAP


2
使い方はとても簡単で、彼らのウェブサイトはロックされています。ただし、それらの構文はあまり直感的ではありません:(myapp myverb -argname argvalue必須-argname)またはmyapp -help(一般的に--help)。
2012年

@Wernightでは、動詞でIsDefaultパラメーターを使用して、省略できます。位置パラメーターのサポートは見つかりませんでしたが、コマンドラインを自分で解析するときに位置パラメーターのみを使用しました。名前付き引数とそれに続く値IMHOを使用する方がはるかに明確です。
Loudenvier

5

この問題には多くの解決策があります。完全性のため、そして誰かが望むなら代替案を提供するために、私は私のグーグルコードライブラリの 2つの有用なクラスにこの答えを追加しています

1つはArgumentListで、コマンドラインパラメータの解析のみを担当します。スイッチ「/ x:y」または「-x = y」で定義された名前と値のペアを収集し、「名前のない」エントリのリストも収集します。基本的な使用法についてはここ説明しますここでクラスを表示してください

この2番目の部分は、.Netクラスから完全に機能するコマンドラインアプリケーションを作成するCommandInterpreterです。例として:

using CSharpTest.Net.Commands;
static class Program
{
    static void Main(string[] args)
    {
        new CommandInterpreter(new Commands()).Run(args);
    }
    //example ‘Commands’ class:
    class Commands
    {
        public int SomeValue { get; set; }
        public void DoSomething(string svalue, int ivalue)
        { ... }

上記のサンプルコードを使用すると、以下を実行できます。

Program.exe DoSomething "文字列値" 5

-または-

Program.exe dosomething / ivalue = 5 -svalue: "string value"

それは、それと同じくらい簡単か、必要なだけ複雑です。あなたはできるソースコードを確認ヘルプを表示する、またはバイナリをダウンロードしてください


4

私のように1つ、あなたが必要な引数の、「ルールを定義」またはことができないので、...

あるいは、あなたがUnixの人なら、GNU Getopt .NETポートが好きかもしれません。


4

あなたは私の1つのRug.Cmdが好きかもしれませ

使いやすく拡張可能なコマンドライン引数パーサー。ハンドル:ブール、プラス/マイナス、文字列、文字列リスト、CSV、列挙。

組み込みの「/?」ヘルプモード。

組み込みの「/ ??」および「/?D」ドキュメントジェネレータモード。

static void Main(string[] args) 
{            
    // create the argument parser
    ArgumentParser parser = new ArgumentParser("ArgumentExample", "Example of argument parsing");

    // create the argument for a string
    StringArgument StringArg = new StringArgument("String", "Example string argument", "This argument demonstrates string arguments");

    // add the argument to the parser 
    parser.Add("/", "String", StringArg);

    // parse arguemnts
    parser.Parse(args);

    // did the parser detect a /? argument 
    if (parser.HelpMode == false) 
    {
        // was the string argument defined 
        if (StringArg.Defined == true)
        {
            // write its value
            RC.WriteLine("String argument was defined");
            RC.WriteLine(StringArg.Value);
        }
    }
}

編集:これは私のプロジェクトであるため、この回答は第三者からの承認と見なしてはなりません。それは私が書くすべてのコマンドラインベースのプログラムにそれを使用することを言った、それはオープンソースであり、他の人がそれから利益を得るかもしれないことを願っています。


参考までに、Rug.Cmdプロジェクトに関連していることを少し免責する必要があります(FAQに記載されています)。stackoverflow.com/ faq#promotion - open-ソースプロジェクトですが、免責事項を追加するのは良いことです;)ちなみに+1 ...かなりうまくいったようです。
Jason Down、

それを指摘してくれてありがとう、そして+1に感謝します。私は私の所属についてより明確にするようにします。
Phill Tew、2012年

心配しないで...この種のものにはステッカーがいくつかあります(私はそのうちの1人ではありません)。繰り返しますが、通常はオープンソースプロジェクトでは問題になりません。それは主に人々が彼らの(有料の)製品のための推薦をスパムすることを止めることです。
Jason Down、

3

http://www.codeplex.com/commonlibrarynetにコマンドライン引数パーサーがあります


1.属性
2.明示的な呼び出し
3.複数の引数の単一行または文字列配列を使用して引数を解析できます。

次のようなものを処理できます。

- 設定:QAから- STARTDATE:$ { 今日 } - 地域:「ニューヨークのSettings01

使い方はとても簡単です。


2

これは、Novell Optionsクラスに基づいて作成したハンドラーです。

これは、while (input !="exit")スタイルループを実行するコンソールアプリケーション、たとえばFTPコンソールなどのインタラクティブコンソールを対象としています。

使用例:

static void Main(string[] args)
{
    // Setup
    CommandHandler handler = new CommandHandler();
    CommandOptions options = new CommandOptions();

    // Add some commands. Use the v syntax for passing arguments
    options.Add("show", handler.Show)
        .Add("connect", v => handler.Connect(v))
        .Add("dir", handler.Dir);

    // Read lines
    System.Console.Write(">");
    string input = System.Console.ReadLine();

    while (input != "quit" && input != "exit")
    {
        if (input == "cls" || input == "clear")
        {
            System.Console.Clear();
        }
        else
        {
            if (!string.IsNullOrEmpty(input))
            {
                if (options.Parse(input))
                {
                    System.Console.WriteLine(handler.OutputMessage);
                }
                else
                {
                    System.Console.WriteLine("I didn't understand that command");
                }

            }

        }

        System.Console.Write(">");
        input = System.Console.ReadLine();
    }
}

そしてソース:

/// <summary>
/// A class for parsing commands inside a tool. Based on Novell Options class (http://www.ndesk.org/Options).
/// </summary>
public class CommandOptions
{
    private Dictionary<string, Action<string[]>> _actions;
    private Dictionary<string, Action> _actionsNoParams;

    /// <summary>
    /// Initializes a new instance of the <see cref="CommandOptions"/> class.
    /// </summary>
    public CommandOptions()
    {
        _actions = new Dictionary<string, Action<string[]>>();
        _actionsNoParams = new Dictionary<string, Action>();
    }

    /// <summary>
    /// Adds a command option and an action to perform when the command is found.
    /// </summary>
    /// <param name="name">The name of the command.</param>
    /// <param name="action">An action delegate</param>
    /// <returns>The current CommandOptions instance.</returns>
    public CommandOptions Add(string name, Action action)
    {
        _actionsNoParams.Add(name, action);
        return this;
    }

    /// <summary>
    /// Adds a command option and an action (with parameter) to perform when the command is found.
    /// </summary>
    /// <param name="name">The name of the command.</param>
    /// <param name="action">An action delegate that has one parameter - string[] args.</param>
    /// <returns>The current CommandOptions instance.</returns>
    public CommandOptions Add(string name, Action<string[]> action)
    {
        _actions.Add(name, action);
        return this;
    }

    /// <summary>
    /// Parses the text command and calls any actions associated with the command.
    /// </summary>
    /// <param name="command">The text command, e.g "show databases"</param>
    public bool Parse(string command)
    {
        if (command.IndexOf(" ") == -1)
        {
            // No params
            foreach (string key in _actionsNoParams.Keys)
            {
                if (command == key)
                {
                    _actionsNoParams[key].Invoke();
                    return true;
                }
            }
        }
        else
        {
            // Params
            foreach (string key in _actions.Keys)
            {
                if (command.StartsWith(key) && command.Length > key.Length)
                {

                    string options = command.Substring(key.Length);
                    options = options.Trim();
                    string[] parts = options.Split(' ');
                    _actions[key].Invoke(parts);
                    return true;
                }
            }
        }

        return false;
    }
}

2

私の個人的なお気に入りは、Peter Palotasによるhttp://www.codeproject.com/KB/recipes/plossum_commandline.aspxです。

[CommandLineManager(ApplicationName="Hello World",
    Copyright="Copyright (c) Peter Palotas")]
class Options
{
   [CommandLineOption(Description="Displays this help text")]
   public bool Help = false;

   [CommandLineOption(Description = "Specifies the input file", MinOccurs=1)]
   public string Name
   {
      get { return mName; }
      set
      {
         if (String.IsNullOrEmpty(value))
            throw new InvalidOptionValueException(
                "The name must not be empty", false);
         mName = value;
      }
   }

   private string mName;
}

2

最近、FubuCoreコマンドライン解析の実装に出会いました。本当に気に入っています。その理由は次のとおりです。

  • 使い方は簡単です。ドキュメントは見つかりませんでしたが、FubuCoreソリューションでは、ユニットテストの優れたセットを含むプロジェクトも提供しています。
  • それは素晴らしいオブジェクト指向のデザインで、コードの繰り返しなど、コマンドライン解析アプリで以前使用していたようなものはありません
  • 宣言型です。基本的に、コマンドのクラスとパラメータのセットを記述し、それらを属性で装飾して、さまざまなオプション(名前、説明、必須/オプションなど)を設定します。
  • ライブラリは、これらの定義に基づいて、素晴らしい使用状況グラフを印刷します

以下は、これを使用する簡単な例です。使い方を説明するために、2つのコマンドを含む簡単なユーティリティを作成しました。現在追加されているすべてのオブジェクト)

まず、「add」コマンドのコマンドクラスを作成しました。

[Usage("add", "Adds an object to the list")]
[CommandDescription("Add object", Name = "add")]
public class AddCommand : FubuCommand<CommandInput>
{
    public override bool Execute(CommandInput input)
    {
        State.Objects.Add(input); // add the new object to an in-memory collection

        return true;
    }
}

このコマンドはCommandInputインスタンスをパラメーターとして取るので、次にそれを定義します。

public class CommandInput
{
    [RequiredUsage("add"), Description("The name of the object to add")]
    public string ObjectName { get; set; }

    [ValidUsage("add")]
    [Description("The value of the object to add")]
    public int ObjectValue { get; set; }

    [Description("Multiply the value by -1")]
    [ValidUsage("add")]
    [FlagAlias("nv")]
    public bool NegateValueFlag { get; set; }
}

次のコマンドは 'list'で、次のように実装されています。

[Usage("list", "List the objects we have so far")]
[CommandDescription("List objects", Name = "list")]
public class ListCommand : FubuCommand<NullInput>
{
    public override bool Execute(NullInput input)
    {
        State.Objects.ForEach(Console.WriteLine);

        return false;
    }
}

'list'コマンドはパラメーターを取らないため、このためにNullInputクラスを定義しました。

public class NullInput { }

あとは、次のようにMain()メソッドに接続するだけです。

    static void Main(string[] args)
    {
        var factory = new CommandFactory();
        factory.RegisterCommands(typeof(Program).Assembly);

        var executor = new CommandExecutor(factory);

        executor.Execute(args);
    }

プログラムは期待どおりに動作し、コマンドが無効な場合の正しい使用法に関するヒントを出力します。

  ------------------------
    Available commands:
  ------------------------
     add -> Add object
    list -> List objects
  ------------------------

そして 'add'コマンドの使用例:

Usages for 'add' (Add object)
  add <objectname> [-nv]

  -------------------------------------------------
    Arguments
  -------------------------------------------------
     objectname -> The name of the object to add
    objectvalue -> The value of the object to add
  -------------------------------------------------

  -------------------------------------
    Flags
  -------------------------------------
    [-nv] -> Multiply the value by -1
  -------------------------------------

2

Powershellコマンドレット。

コマンドレットで指定された属性、検証のサポート、パラメーターセット、パイプライン処理、エラー報告、ヘルプ、および他のコマンドレットで使用するために返されるすべての.NETオブジェクトのベストに基づいて、Powershellによって行われる解析。

はじめに役立つリンクをいくつか紹介します。


2

C#CLIは、私が作成した非常にシンプルなコマンドライン引数解析ライブラリです。十分に文書化され、オープンソースです。


よく文書化されていますか?ドキュメントはどこにありますか?
Suhas 2013年

内部ドキュメント(つまり、コードベース内)と外部ドキュメント(フォルダー内のReadme.mkdファイルを参照Documentation)があります。
Bernard

わかりました、私は急いでコメントしました。プロジェクトをgithubに移動すると、ドキュメントがホームページに自動的に表示されるようになる可能性があります。
Suhas 2013年

1

ジンギスコマンドラインパーサー は少し古くなっているかもしれませんが、非常に完全な機能であり、私にとっては非常にうまく機能します。


残念ですが、ジンギスコマンドラインパーサーにはドキュメントがありません。
okigan 2012年

ソースを見ると、使用オプションを示すサンプルがあります。genghis.codeplex.com/SourceControl/changeset/view/9491#73699
devdimi

0

オープンソースのライブラリCSharpOptParseをお勧めします。コマンドラインを解析し、ユーザー定義の.NETオブジェクトをコマンドライン入力でハイドレートします。C#コンソールアプリケーションを作成するときは、常にこのライブラリを使用します。



0

コマンドライン解析用の非常にシンプルで使いやすいアドホッククラスで、デフォルトの引数をサポートしています。

class CommandLineArgs
{
    public static CommandLineArgs I
    {
        get
        {
            return m_instance;
        }
    }

    public  string argAsString( string argName )
    {
        if (m_args.ContainsKey(argName)) {
            return m_args[argName];
        }
        else return "";
    }

    public long argAsLong(string argName)
    {
        if (m_args.ContainsKey(argName))
        {
            return Convert.ToInt64(m_args[argName]);
        }
        else return 0;
    }

    public double argAsDouble(string argName)
    {
        if (m_args.ContainsKey(argName))
        {
            return Convert.ToDouble(m_args[argName]);
        }
        else return 0;
    }

    public void parseArgs(string[] args, string defaultArgs )
    {
        m_args = new Dictionary<string, string>();
        parseDefaults(defaultArgs );

        foreach (string arg in args)
        {
            string[] words = arg.Split('=');
            m_args[words[0]] = words[1];
        }
    }

    private void parseDefaults(string defaultArgs )
    {
        if ( defaultArgs == "" ) return;
        string[] args = defaultArgs.Split(';');

        foreach (string arg in args)
        {
            string[] words = arg.Split('=');
            m_args[words[0]] = words[1];
        }
    }

    private Dictionary<string, string> m_args = null;
    static readonly CommandLineArgs m_instance = new CommandLineArgs();
}

class Program
{
    static void Main(string[] args)
    {
        CommandLineArgs.I.parseArgs(args, "myStringArg=defaultVal;someLong=12");
        Console.WriteLine("Arg myStringArg  : '{0}' ", CommandLineArgs.I.argAsString("myStringArg"));
        Console.WriteLine("Arg someLong     : '{0}' ", CommandLineArgs.I.argAsLong("someLong"));
    }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.