次の方法で共有


の解析と呼び出し System.CommandLine

Von Bedeutung

System.CommandLine は現在プレビュー段階であり、このドキュメントはバージョン 2.0 ベータ 5 用です。 一部の情報は、リリース前に大幅に変更される可能性があるプレリリース製品に関連しています。 Microsoft は、ここで提供される情報に関して明示的または黙示的な保証を行いません。

System.CommandLine は、コマンド ライン解析とアクション呼び出しの明確な分離を提供します。 解析プロセスは、コマンド ライン入力を解析し、解析された値 (および解析エラー) を含む System.CommandLine.ParseResult オブジェクトを作成します。 アクション呼び出しプロセスは、解析されたコマンド、オプション、またはディレクティブに関連付けられているアクションを呼び出す役割を担います (引数にアクションを含めることはできません)。

System.CommandLineの概要チュートリアルの次の例では、コマンド ライン入力を解析してParseResultを作成します。 アクションは定義または呼び出されません。

using System.CommandLine;
using System.CommandLine.Parsing;

namespace scl;

class Program
{
    static int Main(string[] args)
    {
        Option<FileInfo> fileOption = new("--file")
        {
            Description = "The file to read and display on the console."
        };

        RootCommand rootCommand = new("Sample app for System.CommandLine");
        rootCommand.Options.Add(fileOption);

        ParseResult parseResult = rootCommand.Parse(args);
        if (parseResult.GetValue(fileOption) is FileInfo parsedFile)
        {
            ReadFile(parsedFile);
            return 0;
        }
        foreach (ParseError parseError in parseResult.Errors)
        {
            Console.Error.WriteLine(parseError.Message);
        }
        return 1;
    }

    static void ReadFile(FileInfo file)
    {
        foreach (string line in File.ReadLines(file.FullName))
        {
            Console.WriteLine(line);
        }
    }
}

特定のコマンド (またはディレクティブ、またはオプション) が正常に解析されると、アクションが呼び出されます。 アクションは、 System.CommandLine.ParseResult パラメーターを受け取り、 int 終了コードを返すデリゲートです (非同期アクションも 使用できます)。 終了コードは System.CommandLine.Parsing.ParseResult.Invoke メソッドによって返され、コマンドが正常に実行されたかどうかを示すために使用できます。

System.CommandLineの概要チュートリアルの次の例では、アクションはルート コマンドに対して定義され、コマンド ライン入力の解析後に呼び出されます。

using System.CommandLine;

namespace scl;

class Program
{
    static int Main(string[] args)
    {
        Option<FileInfo> fileOption = new("--file")
        {
            Description = "The file to read and display on the console."
        };

        RootCommand rootCommand = new("Sample app for System.CommandLine");
        rootCommand.Options.Add(fileOption);

        rootCommand.SetAction(parseResult =>
        {
            FileInfo parsedFile = parseResult.GetValue(fileOption);
            ReadFile(parsedFile);
            return 0;
        });

        ParseResult parseResult = rootCommand.Parse(args);
        return parseResult.Invoke();
    }

    static void ReadFile(FileInfo file)
    {
        foreach (string line in File.ReadLines(file.FullName))
        {
            Console.WriteLine(line);
        }
    }
}

System.CommandLine.Help.HelpOptionSystem.CommandLine.VersionOptionSystem.CommandLine.Completions.SuggestDirectiveなどの一部の組み込みシンボルには、定義済みのアクションが付属しています。 これらのシンボルは、ルート コマンドの作成時に自動的に追加され、 System.CommandLine.Parsing.ParseResultを呼び出すと"動作"します。アクションを使用すると、アプリ ロジックに集中できます。一方、ライブラリでは組み込みシンボルのアクションの解析と呼び出しが行われます。 必要に応じて、解析プロセスにこだわり、(上記の最初の例のように) アクションを定義することはできません。

解析結果

System.CommandLine.Parsing.ParseResult型は、コマンド ライン入力の解析結果を表すクラスです。 これを使用して、(アクションを使用しているかどうかにかかわらず) オプションと引数の解析された値を取得する必要があります。 解析エラーや一致しない トークンがあるかどうかを確認することもできます。

値を取得

System.CommandLine.Parsing.ParseResult.GetValue<T> メソッドを使用すると、オプションと引数の値を取得できます。

int integer = parseResult.GetValue(delayOption);
string? message = parseResult.GetValue(messageOption);

名前で値を取得することもできますが、取得する値の型を指定する必要があります。

次の例では、C# コレクション初期化子を使用してルート コマンドを作成します。

RootCommand rootCommand = new("Parameter binding example")
{
    new Option<int>("--delay")
    {
        Description = "An option whose argument is parsed as an int."
    },
    new Option<string>("--message")
    {
        Description = "An option whose argument is parsed as a string."
    }
};

次に、 GetValue メソッドを使用して、名前で値を取得します。

rootCommand.SetAction(parseResult =>
{
    int integer = parseResult.GetValue<int>("--delay");
    string? message = parseResult.GetValue<string>("--message");

    DisplayIntAndString(integer, message);
});

GetValueのこのオーバーロードは、解析されたコマンドのコンテキスト (シンボル ツリー全体ではなく) で、指定されたシンボル名の解析済みまたは既定値を取得します。 エイリアスではなくシンボル名を受け入れます。

解析エラー

System.CommandLine.Parsing.ParseResult.Errors プロパティには、解析プロセス中に発生した解析エラーの一覧が含まれています。 各エラーは、エラー メッセージやエラーの原因となったトークンなど、エラーに関する情報を含む System.CommandLine.Parsing.ParseError オブジェクトによって表されます。

System.CommandLine.Parsing.ParseResult.Invoke メソッドを呼び出すと、解析が成功したかどうかを示す終了コードが返されます。 解析エラーが発生した場合、終了コードは 0 以外であり、すべての解析エラーが標準エラーに出力されます。

System.CommandLine.Parsing.ParseResult.Invoke メソッドを呼び出していない場合は、エラーを印刷するなどして、独自に処理する必要があります。

foreach (ParseError parseError in parseResult.Errors)
{
    Console.Error.WriteLine(parseError.Message);
}
return 1;

一致しないトークン

System.CommandLine.Parsing.ParseResult.UnmatchedTokens プロパティには、解析されたが、構成されているコマンド、オプション、または引数と一致しなかったトークンの一覧が含まれています。

一致しないトークンの一覧は、ラッパーのように動作するコマンドで役立ちます。 ラッパー コマンドは、トークンのセット 受け取り、別のコマンドまたはアプリに転送します。 Linux の sudo コマンドの例を示します。 偽装するユーザーの名前を受け取り、その後に実行するコマンドを受け取ります。 例えば次が挙げられます。

sudo -u admin apt update

このコマンド ラインでは、ユーザー adminとして apt update コマンドが実行されます。

このようなラッパー コマンドを実装するには、コマンド プロパティ System.CommandLine.Command.TreatUnmatchedTokensAsErrorsfalse に設定します。 その後、 System.CommandLine.Parsing.ParseResult.UnmatchedTokens プロパティには、コマンドに明示的に属していないすべての引数が含まれます。 前の例では、 ParseResult.UnmatchedTokens には apt トークンと update トークンが含まれています。

アクション

アクションは、コマンド (またはオプションまたはディレクティブ) が正常に解析されたときに呼び出されるデリゲートです。 System.CommandLine.ParseResult パラメーターを受け取り、int (またはTask<int>) 終了コードを返します。 終了コードは、アクションが正常に実行されたかどうかを示すために使用されます。

System.CommandLine には、抽象基底クラス System.CommandLine.CommandLineAction と 2 つの派生クラス ( System.CommandLine.SynchronousCommandLineActionSystem.CommandLine.AsynchronousCommandLineAction) が用意されています。 前者は int 終了コードを返す同期アクションに使用され、後者は Task<int> 終了コードを返す非同期アクションに使用されます。

アクションを定義するために派生型を作成する必要はありません。 System.CommandLine.Command.SetAction メソッドを使用して、コマンドのアクションを設定できます。 同期アクションは、 System.CommandLine.ParseResult パラメーターを受け取り、 int 終了コードを返すデリゲートにすることができます。 非同期アクションは、 System.CommandLine.ParseResultCancellationToken パラメーターを受け取り、 Task<int>を返すデリゲートにすることができます。

rootCommand.SetAction(parseResult =>
{
    FileInfo parsedFile = parseResult.GetValue(fileOption);
    ReadFile(parsedFile);
    return 0;
});

非同期アクション

同期アクションと非同期アクションを同じアプリケーションで混在させるべきではありません。 非同期アクションを使用する場合は、アプリケーションを上から下に非同期にする必要があります。 つまり、すべてのアクションは非同期であり、Task<int>終了コードを返すデリゲートを受け入れるSystem.CommandLine.Command.SetAction メソッドを使用する必要があります。 さらに、アクション デリゲートに渡される CancellationToken は、ファイル I/O 操作やネットワーク要求など、取り消すことができるすべてのメソッドにさらに渡す必要があります。

その上で、System.CommandLine.Parsing.ParseResult.Invokeの代わりに System.CommandLine.Parsing.ParseResult.InvokeAsync メソッドが使用されていることを確認する必要があります。 このメソッドは非同期であり、 Task<int> 終了コードを返します。 また、アクションの取り消しに使用できる省略可能な CancellationToken パラメーターも受け取ります。

上記のコードでは、単なるParseResultではなく、ParseResultCancellationTokenを取得するSetAction オーバーロードを使用しています。

static Task<int> Main(string[] args)
{
    Option<string> urlOption = new("--url", "A URL.");
    RootCommand rootCommand = new("Handle termination example") { urlOption };

    rootCommand.SetAction((ParseResult parseResult, CancellationToken cancellationToken) =>
    {
        string? urlOptionValue = parseResult.GetValue(urlOption);
        return DoRootCommand(urlOptionValue, cancellationToken);
    });

    return rootCommand.Parse(args).InvokeAsync();
}

public static async Task<int> DoRootCommand(
    string? urlOptionValue, CancellationToken cancellationToken)
{
    using HttpClient httpClient = new();

    try
    {
        await httpClient.GetAsync(urlOptionValue, cancellationToken);
        return 0;
    }
    catch (OperationCanceledException)
    {
        await Console.Error.WriteLineAsync("The operation was aborted");
        return 1;
    }
}

プロセス終了タイムアウト

System.CommandLine.CommandLineConfiguration.ProcessTerminationTimeoutでは、呼び出し中にすべての非同期アクションに渡されるCancellationTokenを介して、プロセス終了 (Ctrl+CSIGINTSIGTERM) のシグナル通知と処理が可能になります。 既定では (2 秒) 有効になっていますが、 null に設定して無効にすることができます。

有効にすると、指定したタイムアウト内にアクションが完了しない場合、プロセスは終了します。 これは、プロセスが終了する前の状態を保存するなどして、終了を適切に処理する場合に役立ちます。

前の段落のサンプル コードをテストするには、読み込みに少し時間がかかる URL でコマンドを実行し、読み込みが完了する前に Ctrl+Cキーを押します。 macOS で Command+Period(.)キーを押します。 例えば次が挙げられます。

testapp --url https://learn.microsoft.com/aspnet/core/fundamentals/minimal-apis
The operation was aborted

終了コード

終了コードは、成功または失敗を示すアクションによって返される整数値です。 慣例により、 0 の終了コードは成功を示し、ゼロ以外の値はエラーを示します。 コマンド実行の状態を明確に伝えるために、アプリケーションで意味のある終了コードを定義することが重要です。

すべての SetAction メソッドには、明示的な方法で終了コードを指定する必要がある int 終了コードと、 0を返すオーバーロードを返すデリゲートを受け入れるオーバーロードがあります。

static int Main(string[] args)
{
    Option<int> delayOption = new("--delay");
    Option<string> messageOption = new("--message");

    RootCommand rootCommand = new("Parameter binding example")
    {
        delayOption,
        messageOption
    };

    rootCommand.SetAction(parseResult =>
    {
        Console.WriteLine($"--delay = {parseResult.GetValue(delayOption)}");
        Console.WriteLine($"--message = {parseResult.GetValue(messageOption)}");
        // Value returned from the action delegate is the exit code.
        return 100;
    });

    return rootCommand.Parse(args).Invoke();
}

こちらも参照ください

System.CommandLineで解析と検証をカスタマイズする方法System.CommandLineの概要