Freigeben über


Lernprogramm: Erste Schritte mit System.CommandLine

Wichtig

System.CommandLine befindet sich derzeit in der Vorschau, und diese Dokumentation bezieht sich auf die Version 2.0 beta 5. Einige Informationen beziehen sich auf vorab veröffentlichte Produkte, die vor der Veröffentlichung erheblich geändert werden können. Microsoft gibt keine Garantie, weder ausdrücklich noch impliziert, hinsichtlich der hier bereitgestellten Informationen.

In diesem Lernprogramm wird gezeigt, wie Sie eine .NET-Befehlszeilen-App erstellen, die die System.CommandLine Bibliothek verwendet. Sie beginnen mit dem Erstellen eines einfachen Stammbefehls, der über eine Option verfügt. Anschließend erstellen Sie auf dieser Basis eine komplexere App, die mehrere Unterbefehle und unterschiedliche Optionen für jeden Befehl enthält.

In diesem Tutorial lernen Sie Folgendes:

  • Erstellen Sie Befehle, Optionen und Argumente.
  • Geben Sie Standardwerte für Optionen an.
  • Weisen Sie Befehlen Optionen und Argumente zu.
  • Weisen Sie allen Unterbefehlen unter einem Befehl rekursiv eine Option zu.
  • Arbeiten Sie mit mehreren Ebenen geschachtelter Unterbefehle.
  • Erstellen Sie Aliase für Befehle und Optionen.
  • Arbeiten Sie mit string, string[], int, bool, FileInfo und enum Optionstypen.
  • Lesen von Optionswerten im Befehlsaktionscode.
  • Verwenden Sie benutzerdefinierten Code zum Analysieren und Überprüfen von Optionen.

Voraussetzungen

Oder

Erstellen der App

Erstellen Sie ein .NET 9-Konsolen-App-Projekt mit dem Namen "scl".

  1. Erstellen Sie einen Ordner mit dem Namen "scl " für das Projekt, und öffnen Sie dann eine Eingabeaufforderung im neuen Ordner.

  2. Führen Sie den folgenden Befehl aus:

    dotnet new console --framework net9.0
    

Installieren Sie das System.CommandLine-Paket

  • Führen Sie den folgenden Befehl aus:

    dotnet add package System.CommandLine --prerelease
    

    Oder in .NET 10+:

    dotnet package add System.CommandLine --prerelease
    

    Die --prerelease Option ist erforderlich, da sich die Bibliothek noch in der Betaversion befindet.

Analysieren der Argumente

  1. Ersetzen Sie den Inhalt von Program.cs durch den folgenden Code:

    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);
            }
        }
    }
    

Der vorherige Code:

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);
  • Analysiert args und überprüft, ob ein beliebiger Wert für die Option --file bereitgestellt wurde. Wenn dies der Fall ist, ruft sie die ReadFile Methode mit analysierten Werten auf und gibt Exit-Code zurück 0 :
ParseResult parseResult = rootCommand.Parse(args);
if (parseResult.GetValue(fileOption) is FileInfo parsedFile)
{
    ReadFile(parsedFile);
    return 0;
}
  • Wenn kein Wert für --file angegeben wurde, werden verfügbare Analysefehler ausgegeben und der Exit-Code 1 zurückgegeben.
foreach (ParseError parseError in parseResult.Errors)
{
    Console.Error.WriteLine(parseError.Message);
}
return 1;
  • Die ReadFile Methode liest die angegebene Datei und zeigt den Inhalt auf der Konsole an:
static void ReadFile(FileInfo file)
{
    foreach (string line in File.ReadLines(file.FullName))
    {
        Console.WriteLine(line);
    }
}

Testen der App

Sie können eine der folgenden Möglichkeiten zum Testen während der Entwicklung einer Befehlszeilen-App verwenden:

  • Führen Sie den dotnet build Befehl aus, und öffnen Sie dann eine Eingabeaufforderung im Ordner "scl/bin/Debug/net9.0 ", um die ausführbare Datei auszuführen:

    dotnet build
    cd bin/Debug/net9.0
    scl --file scl.runtimeconfig.json
    
  • Verwenden Sie dotnet run, und übergeben Sie Optionswerte anstelle des run-Befehls an die App, indem Sie sie wie im folgenden Beispiel gezeigt nach -- einschließen:

    dotnet run -- --file bin/Debug/net9.0/scl.runtimeconfig.json
    

In diesem Lernprogramm wird davon ausgegangen, dass Sie die erste dieser Optionen verwenden.

Wenn Sie die App ausführen, wird der Inhalt der durch die --file Option angegebenen Datei angezeigt.

{
  "runtimeOptions": {
    "tfm": "net9.0",
    "framework": {
      "name": "Microsoft.NETCore.App",
      "version": "9.0.0"
    }
  }
}

Aber was passiert, wenn Sie es mit --help auffordern, die Hilfe anzuzeigen? Auf der Konsole wird nichts ausgegeben, weil die App das Szenario, bei dem --file nicht angegeben ist und keine Parsefehler vorliegen, noch nicht behandelt.

Analysieren sie die Argumente, und rufen Sie das ParseResult auf.

System.CommandLine ermöglicht es Ihnen, eine Aktion anzugeben, die aufgerufen wird, wenn ein bestimmtes Symbol (Befehl, Direktive oder Option) erfolgreich analysiert wird. Die Aktion ist ein Delegat, der einen System.CommandLine.ParseResult Parameter verwendet und einen int Exitcode zurückgibt (asynchrone Aktionen sind ebenfalls verfügbar). Der Beendigungscode wird von der System.CommandLine.Parsing.ParseResult.Invoke Methode zurückgegeben und kann verwendet werden, um anzugeben, ob der Befehl erfolgreich ausgeführt wurde oder nicht.

  1. Ersetzen Sie den Inhalt von Program.cs durch den folgenden Code:

    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);
            }
        }
    }
    

Der vorherige Code:

  • Gibt die Methode an ReadFile , die aufgerufen wird, wenn der Stammbefehl aufgerufen wird:

    rootCommand.SetAction(parseResult =>
    {
        FileInfo parsedFile = parseResult.GetValue(fileOption);
        ReadFile(parsedFile);
        return 0;
    });
    
  • Parst die args und ruft das Ergebnis auf:

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

Wenn Sie die App ausführen, wird der Inhalt der durch die --file Option angegebenen Datei angezeigt.

Was geschieht, wenn Sie sie bitten, die Hilfe anzuzeigen, indem Sie angeben --help?

scl --help

Die folgende Ausgabe wird ausgegeben:

Description:
  Sample app for System.CommandLine

Usage:
  scl [options]

Options:
  -?, -h, --help  Show help and usage information
  --version       Show version information
  --file          The file to read and display on the conso

System.CommandLine.RootCommand stellt standardmäßig Hilfeoption, Versionsoption und Vorschlagsdirektive bereit. ParseResult.Invoke die Methode ist für das Aufrufen der Aktion des analysierten Symbols verantwortlich. Es kann sich entweder um die für unseren Befehl explizit definierte Aktion oder um die Hilfeaktion handeln, die durch System.CommandLine für System.CommandLine.Help.HelpOption definiert ist. Wenn es Parse-Fehler entdeckt, gibt es diese in den Standardfehler aus, gibt die Hilfe in die Standardausgabe aus und gibt einen 1 Exit-Code zurück:

scl --invalid bla
Unrecognized command or argument '--invalid'.
Unrecognized command or argument 'bla'.

Hinzufügen eines Unterbefehls und von Optionen

In diesem Abschnitt führen Sie Folgendes aus:

  • Erstellen Sie weitere Optionen.
  • Erstellen Sie einen Unterbefehl.
  • Weisen Sie dem neuen Unterbefehl die neuen Optionen zu.

Mit den neuen Optionen können Sie die Vordergrund- und Hintergrundtextfarben und die Lesegeschwindigkeit konfigurieren. Diese Features werden verwendet, um eine Sammlung von Zitaten zu lesen, die aus dem Lernprogramm der Teleprompter-Konsolen-App stammen.

  1. Kopieren Sie die sampleQuotes.txt Datei aus dem GitHub-Repository für dieses Beispiel in Ihr Projektverzeichnis. Informationen zum Herunterladen von Dateien finden Sie in den Anweisungen in Beispielen und Lernprogrammen.

  2. Öffnen Sie die Projektdatei, und fügen Sie direkt vor dem schließenden <ItemGroup> Tag ein </Project> Element hinzu:

    <ItemGroup>
      <Content Include="sampleQuotes.txt">
        <CopyToOutputDirectory>Always</CopyToOutputDirectory>
      </Content>
    </ItemGroup>
    

    Wenn Sie dieses Markup hinzufügen, wird die Textdatei beim Erstellen der App in den Ordner "bin/debug/net9.0" kopiert. Wenn Sie also die ausführbare Datei in diesem Ordner ausführen, können Sie nach Namen auf die Datei zugreifen, ohne einen Ordnerpfad anzugeben.

  3. Erstellen Sie in Program.cs nach dem Code, der die --file Option erstellt, Optionen zum Steuern der Lesegeschwindigkeit und der Textfarben:

    Option<int> delayOption = new("--delay")
    {
        Description = "Delay between lines, specified as milliseconds per character in a line.",
        DefaultValueFactory = parseResult => 42
    };
    Option<ConsoleColor> fgcolorOption = new("--fgcolor")
    {
        Description = "Foreground color of text displayed on the console.",
        DefaultValueFactory = parseResult => ConsoleColor.White
    };
    Option<bool> lightModeOption = new("--light-mode")
    {
        Description = "Background color of text displayed on the console: default is black, light mode is white."
    };
    
  4. Löschen Sie nach der Zeile, die den Stammbefehl erstellt, den Code, der die --file Option hinzufügt. Sie entfernen es hier, da Sie ihn einem neuen Unterbefehl hinzufügen.

  5. Erstellen Sie nach der Zeile, die den Stammbefehl erstellt, einen read Unterbefehl. Fügen Sie diesem Unterbefehl die Optionen hinzu, indem Sie die Sammlungsinitialisierungssyntax anstelle der Options Eigenschaft verwenden, und fügen Sie den Unterbefehl zum Stammbefehl hinzu.

    Command readCommand = new("read", "Read and display the file.")
    {
        fileOption,
        delayOption,
        fgcolorOption,
        lightModeOption
    };
    rootCommand.Subcommands.Add(readCommand);
    
  6. Ersetzen Sie den SetAction Code durch den folgenden SetAction Code für den neuen Unterbefehl:

    readCommand.SetAction(parseResult => ReadFile(
        parseResult.GetValue(fileOption),
        parseResult.GetValue(delayOption),
        parseResult.GetValue(fgcolorOption),
        parseResult.GetValue(lightModeOption)));
    

    Sie rufen den Hauptbefehl SetAction nicht mehr auf, da der Hauptbefehl keine Aktion mehr erfordert. Wenn ein Befehl Unterbefehle aufweist, müssen Sie in der Regel einen der Unterbefehle angeben, wenn Sie eine Befehlszeilen-App aufrufen.

  7. Ersetzen Sie die ReadFile Aktionsmethode durch den folgenden Code:

    internal static void ReadFile(FileInfo file, int delay, ConsoleColor fgColor, bool lightMode)
    {
        Console.BackgroundColor = lightMode ? ConsoleColor.White : ConsoleColor.Black;
        Console.ForegroundColor = fgColor;
        foreach (string line in File.ReadLines(file.FullName))
        {
            Console.WriteLine(line);
            Thread.Sleep(TimeSpan.FromMilliseconds(delay * line.Length));
        }
    }
    

Die App sieht nun wie folgt aus:

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."
        };

        Option<int> delayOption = new("--delay")
        {
            Description = "Delay between lines, specified as milliseconds per character in a line.",
            DefaultValueFactory = parseResult => 42
        };
        Option<ConsoleColor> fgcolorOption = new("--fgcolor")
        {
            Description = "Foreground color of text displayed on the console.",
            DefaultValueFactory = parseResult => ConsoleColor.White
        };
        Option<bool> lightModeOption = new("--light-mode")
        {
            Description = "Background color of text displayed on the console: default is black, light mode is white."
        };

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

        Command readCommand = new("read", "Read and display the file.")
        {
            fileOption,
            delayOption,
            fgcolorOption,
            lightModeOption
        };
        rootCommand.Subcommands.Add(readCommand);

        readCommand.SetAction(parseResult => ReadFile(
            parseResult.GetValue(fileOption),
            parseResult.GetValue(delayOption),
            parseResult.GetValue(fgcolorOption),
            parseResult.GetValue(lightModeOption)));

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

    internal static void ReadFile(FileInfo file, int delay, ConsoleColor fgColor, bool lightMode)
    {
        Console.BackgroundColor = lightMode ? ConsoleColor.White : ConsoleColor.Black;
        Console.ForegroundColor = fgColor;
        foreach (string line in File.ReadLines(file.FullName))
        {
            Console.WriteLine(line);
            Thread.Sleep(TimeSpan.FromMilliseconds(delay * line.Length));
        }
    }
}

Testen des neuen Unterbefehls

Wenn Sie nun versuchen, die App auszuführen, ohne den Unterbefehl anzugeben, wird eine Fehlermeldung gefolgt von einer Hilfemeldung angezeigt, die den verfügbaren Unterbefehl angibt.

scl --file sampleQuotes.txt
'--file' was not matched. Did you mean one of the following?
--help

Required command was not provided.
Unrecognized command or argument '--file'.
Unrecognized command or argument 'sampleQuotes.txt'.

Description:
  Sample app for System.CommandLine

Usage:
  scl [command] [options]

Options:
  -?, -h, --help  Show help and usage information
  --version       Show version information

Commands:
  read  Read and display the file.

Der Hilfetext für den Unterbefehl read zeigt, dass vier Optionen verfügbar sind. Es werden gültige Werte für die Enumeration angezeigt.

scl read -h
Description:
  Read and display the file.

Usage:
  scl read [options]

Options:
  --file <file>                                               The file to read and display on the console.
  --delay <delay>                                             Delay between lines, specified as milliseconds per
                                                              character in a line. [default: 42]
  --fgcolor                                                   Foreground color of text displayed on the console.
  <Black|Blue|Cyan|DarkBlue|DarkCyan|DarkGray|DarkGreen|Dark  [default: White]
  Magenta|DarkRed|DarkYellow|Gray|Green|Magenta|Red|White|Ye
  llow>
  --light-mode                                                Background color of text displayed on the console:
                                                              default is black, light mode is white.
  -?, -h, --help                                              Show help and usage information

Führen Sie den Unterbefehl read aus, der nur die --file Option angibt, und Sie erhalten die Standardwerte für die anderen drei Optionen.

scl read --file sampleQuotes.txt

Die Standardverzögerung von 42 Millisekunden pro Zeichen verursacht eine langsame Lesegeschwindigkeit. Sie können dies beschleunigen, indem Sie eine niedrigere Zahl festlegen --delay .

scl read --file sampleQuotes.txt --delay 0

Sie können --fgcolor und --light-mode verwenden, um Textfarben festzulegen.

scl read --file sampleQuotes.txt --fgcolor red --light-mode

Geben Sie einen ungültigen Wert ein --delay , und Sie erhalten eine Fehlermeldung:

scl read --file sampleQuotes.txt --delay forty-two
Cannot parse argument 'forty-two' for option '--int' as expected type 'System.Int32'.

Geben Sie einen ungültigen Wert ein --file , und Sie erhalten eine Ausnahme:

scl read --file nofile
Unhandled exception: System.IO.FileNotFoundException: Could not find file 'C:\bin\Debug\net9.0\nofile''.
File name: 'C:\bin\Debug\net9.0\nofile''

Hinzufügen von Unterbefehlen und benutzerdefinierter Überprüfung

In diesem Abschnitt wird die endgültige Version der App erstellt. Nach Abschluss des Vorgangs verfügt die App über die folgenden Befehle und Optionen:

  • Stammbefehl mit einer rekursiven* Option namens --file
    • quotes Befehl
      • read Befehl mit optionen mit dem Namen --delay, --fgcolor, und --light-mode
      • add Befehl mit benannten quote Argumenten und byline
      • delete Befehl mit dem Namen der Option --search-terms

* Eine rekursive Option ist für den Befehl verfügbar, dem sie zugewiesen ist, und rekursiv für alle seine Unterbefehle.

Hier sehen Sie eine Beispiel-Befehlszeileneingabe, die jeden der verfügbaren Befehle mit den zugehörigen Optionen und Argumenten aufruft:

scl quotes read --file sampleQuotes.txt --delay 40 --fgcolor red --light-mode
scl quotes add "Hello world!" "Nancy Davolio"
scl quotes delete --search-terms David "You can do" Antoine "Perfection is achieved"
  1. Ersetzen Sie in Program.cs den Code, der die --file Option erstellt, durch den folgenden Code:

    Option<FileInfo> fileOption = new("--file")
    {
        Description = "An option whose argument is parsed as a FileInfo",
        Required = true,
        DefaultValueFactory = result =>
        {
            if (result.Tokens.Count == 0)
            {
                return new FileInfo("sampleQuotes.txt");
    
            }
            string filePath = result.Tokens.Single().Value;
            if (!File.Exists(filePath))
            {
                result.AddError("File does not exist");
                return null;
            }
            else
            {
                return new FileInfo(filePath);
            }
        }
    };
    

    Dieser Code verwendet System.CommandLine.Parsing.ArgumentResult , um benutzerdefinierte Analyse, Überprüfung und Fehlerbehandlung bereitzustellen.

    Ohne diesen Code werden fehlende Dateien mit einer Ausnahme und Stapelüberwachung gemeldet. Mit diesem Code wird nur die angegebene Fehlermeldung angezeigt.

    Dieser Code gibt auch einen Standardwert an, weshalb er DefaultValueFactory auf eine benutzerdefinierte Analysemethode setzt.

  2. Fügen Sie nach dem Code, der lightModeOption erstellt, Optionen und Argumente für die add und delete Befehle hinzu.

    Option<string[]> searchTermsOption = new("--search-terms")
    {
        Description = "Strings to search for when deleting entries.",
        Required = true,
        AllowMultipleArgumentsPerToken = true
    };
    Argument<string> quoteArgument = new("quote")
    {
        Description = "Text of quote."
    };
    Argument<string> bylineArgument = new("byline")
    {
        Description = "Byline of quote."
    };
    

    Mit der xref:System.CommandLine.Option.AllowMultipleArgumentsPerToken Einstellung können Sie den --search-terms Optionsnamen weglassen, wenn Sie Elemente in der Liste nach dem ersten angeben. Die folgenden Beispiele für die Befehlszeileneingabe sind gleichwertig:

    scl quotes delete --search-terms David "You can do"
    scl quotes delete --search-terms David --search-terms "You can do"
    
  3. Ersetzen Sie den Code, der den Stammbefehl und den read Befehl erstellt, durch den folgenden Code:

    RootCommand rootCommand = new("Sample app for System.CommandLine");
    fileOption.Recursive = true;
    rootCommand.Options.Add(fileOption);
    
    Command quotesCommand = new("quotes", "Work with a file that contains quotes.");
    rootCommand.Subcommands.Add(quotesCommand);
    
    Command readCommand = new("read", "Read and display the file.")
    {
        delayOption,
        fgcolorOption,
        lightModeOption
    };
    quotesCommand.Subcommands.Add(readCommand);
    
    Command deleteCommand = new("delete", "Delete lines from the file.");
    deleteCommand.Options.Add(searchTermsOption);
    quotesCommand.Subcommands.Add(deleteCommand);
    
    Command addCommand = new("add", "Add an entry to the file.");
    addCommand.Arguments.Add(quoteArgument);
    addCommand.Arguments.Add(bylineArgument);
    addCommand.Aliases.Add("insert");
    quotesCommand.Subcommands.Add(addCommand);
    

    Dieser Code nimmt die folgenden Änderungen vor:

    • Entfernt die --file Option aus dem read Befehl.

    • Fügt die --file Option als rekursive Option zum Stammbefehl hinzu.

    • Erstellt einen quotes Befehl und fügt ihn dem Stammbefehl hinzu.

    • Fügt den read Befehl dem quotes Befehl anstelle des Stammbefehls hinzu.

    • Erstellt add und delete befehle und fügt sie dem quotes Befehl hinzu.

    Das Ergebnis ist die folgende Befehlshierarchie:

    • Stammbefehl
      • quotes
        • read
        • add
        • delete

    Die App implementiert jetzt das empfohlene Muster, bei dem der übergeordnete Befehl (quotes) einen Bereich oder eine Gruppe angibt und die untergeordneten Elemente (read, add, delete) Aktionen sind.

    Rekursive Optionen werden auf den Befehl angewendet und rekursiv auf Unterbefehle angewendet. Da sich --file im Stammbefehl befindet, wird er automatisch in allen Unterbefehlen der App verfügbar sein.

  4. Fügen Sie nach dem SetAction Code neuen SetAction Code für die neuen Unterbefehle hinzu:

    deleteCommand.SetAction(parseResult => DeleteFromFile(
        parseResult.GetValue(fileOption),
        parseResult.GetValue(searchTermsOption)));
    
    addCommand.SetAction(parseResult => AddToFile(
        parseResult.GetValue(fileOption),
        parseResult.GetValue(quoteArgument),
        parseResult.GetValue(bylineArgument))
        );
    

    Der Unterbefehl quotes verfügt nicht über eine Aktion, da es sich nicht um einen Blattbefehl handelt. Unterbefehle read, add, und delete sind Blattbefehle unter quotes, und SetAction wird für jeden von ihnen aufgerufen.

  5. Fügen Sie die Aktionen für add und delete.

    internal static void DeleteFromFile(FileInfo file, string[] searchTerms)
    {
        Console.WriteLine("Deleting from file");
    
        var lines = File.ReadLines(file.FullName).Where(line => searchTerms.All(s => !line.Contains(s)));
        File.WriteAllLines(file.FullName, lines);
    }
    internal static void AddToFile(FileInfo file, string quote, string byline)
    {
        Console.WriteLine("Adding to file");
    
        using StreamWriter writer = file.AppendText();
        writer.WriteLine($"{Environment.NewLine}{Environment.NewLine}{quote}");
        writer.WriteLine($"{Environment.NewLine}-{byline}");
    }
    

Die fertige App sieht wie folgt aus:

using System.CommandLine;

namespace scl;

class Program
{
    static int Main(string[] args)
    {
        Option<FileInfo> fileOption = new("--file")
        {
            Description = "An option whose argument is parsed as a FileInfo",
            Required = true,
            DefaultValueFactory = result =>
            {
                if (result.Tokens.Count == 0)
                {
                    return new FileInfo("sampleQuotes.txt");

                }
                string filePath = result.Tokens.Single().Value;
                if (!File.Exists(filePath))
                {
                    result.AddError("File does not exist");
                    return null;
                }
                else
                {
                    return new FileInfo(filePath);
                }
            }
        };

        Option<int> delayOption = new("--delay")
        {
            Description = "Delay between lines, specified as milliseconds per character in a line.",
            DefaultValueFactory = parseResult => 42
        };
        Option<ConsoleColor> fgcolorOption = new("--fgcolor")
        {
            Description = "Foreground color of text displayed on the console.",
            DefaultValueFactory = parseResult => ConsoleColor.White
        };
        Option<bool> lightModeOption = new("--light-mode")
        {
            Description = "Background color of text displayed on the console: default is black, light mode is white."
        };

        Option<string[]> searchTermsOption = new("--search-terms")
        {
            Description = "Strings to search for when deleting entries.",
            Required = true,
            AllowMultipleArgumentsPerToken = true
        };
        Argument<string> quoteArgument = new("quote")
        {
            Description = "Text of quote."
        };
        Argument<string> bylineArgument = new("byline")
        {
            Description = "Byline of quote."
        };

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

        Command quotesCommand = new("quotes", "Work with a file that contains quotes.");
        rootCommand.Subcommands.Add(quotesCommand);

        Command readCommand = new("read", "Read and display the file.")
        {
            delayOption,
            fgcolorOption,
            lightModeOption
        };
        quotesCommand.Subcommands.Add(readCommand);

        Command deleteCommand = new("delete", "Delete lines from the file.");
        deleteCommand.Options.Add(searchTermsOption);
        quotesCommand.Subcommands.Add(deleteCommand);

        Command addCommand = new("add", "Add an entry to the file.");
        addCommand.Arguments.Add(quoteArgument);
        addCommand.Arguments.Add(bylineArgument);
        addCommand.Aliases.Add("insert");
        quotesCommand.Subcommands.Add(addCommand);

        readCommand.SetAction(parseResult => ReadFile(
            parseResult.GetValue(fileOption),
            parseResult.GetValue(delayOption),
            parseResult.GetValue(fgcolorOption),
            parseResult.GetValue(lightModeOption)));

        deleteCommand.SetAction(parseResult => DeleteFromFile(
            parseResult.GetValue(fileOption),
            parseResult.GetValue(searchTermsOption)));

        addCommand.SetAction(parseResult => AddToFile(
            parseResult.GetValue(fileOption),
            parseResult.GetValue(quoteArgument),
            parseResult.GetValue(bylineArgument))
            );

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

    internal static void ReadFile(FileInfo file, int delay, ConsoleColor fgColor, bool lightMode)
    {
        Console.BackgroundColor = lightMode ? ConsoleColor.White : ConsoleColor.Black;
        Console.ForegroundColor = fgColor;
        foreach (string line in File.ReadLines(file.FullName))
        {
            Console.WriteLine(line);
            Thread.Sleep(TimeSpan.FromMilliseconds(delay * line.Length));
        }
    }
    internal static void DeleteFromFile(FileInfo file, string[] searchTerms)
    {
        Console.WriteLine("Deleting from file");

        var lines = File.ReadLines(file.FullName).Where(line => searchTerms.All(s => !line.Contains(s)));
        File.WriteAllLines(file.FullName, lines);
    }
    internal static void AddToFile(FileInfo file, string quote, string byline)
    {
        Console.WriteLine("Adding to file");

        using StreamWriter writer = file.AppendText();
        writer.WriteLine($"{Environment.NewLine}{Environment.NewLine}{quote}");
        writer.WriteLine($"{Environment.NewLine}-{byline}");
    }
}

Erstellen Sie das Projekt, und versuchen Sie dann die folgenden Befehle.

Übermitteln Sie mit dem --file-Befehl eine nicht vorhandene Datei an read, und Sie erhalten eine Fehlermeldung anstelle einer Ausnahme und Stapelüberwachung:

scl quotes read --file nofile
File does not exist

Versuchen Sie, den Unterbefehl quotes auszuführen, und Sie erhalten eine Nachricht, die Sie zur Verwendung von read, add oder delete auffordert.

scl quotes
Required command was not provided.

Description:
  Work with a file that contains quotes.

Usage:
  scl quotes [command] [options]

Options:
  --file <file>   An option whose argument is parsed as a FileInfo [default: sampleQuotes.txt]
  -?, -h, --help  Show help and usage information

Commands:
  read                          Read and display the file.
  delete                        Delete lines from the file.
  add, insert <quote> <byline>  Add an entry to the file.

Führen Sie den Unterbefehl addaus, und sehen Sie sich dann das Ende der Textdatei an, um den hinzugefügten Text anzuzeigen:

scl quotes add "Hello world!" "Nancy Davolio"

Führen Sie den Unterbefehl delete mit Suchzeichenfolgen am Anfang der Datei aus, und sehen Sie sich dann den Anfang der Textdatei an, um zu sehen, wo Text entfernt wurde:

scl quotes delete --search-terms David "You can do" Antoine "Perfection is achieved"

Hinweis

Wenn Sie den Ordner "bin/debug/net9.0" ausführen, finden Sie in diesem Ordner die Datei mit Änderungen, die durch die Befehle add und delete vorgenommen wurden. Die Kopie der Datei im Projektordner bleibt unverändert.

Nächste Schritte

In diesem Tutorial haben Sie eine einfache Befehlszeilen-App erstellt, die System.CommandLine verwendet. Weitere Informationen zur Bibliothek finden Sie in System.CommandLine der Übersicht.