Udostępnij za pośrednictwem


Samouczek: wprowadzenie do System.CommandLine

Ważne

System.CommandLine jest obecnie dostępna w wersji zapoznawczej, a ta dokumentacja dotyczy wersji 2.0 beta 5. Niektóre informacje odnoszą się do produktu w wersji wstępnej, który może zostać znacząco zmodyfikowany przed jego wydaniem. Firma Microsoft nie udziela żadnych gwarancji, wyraźnych ani domniemanych, w odniesieniu do podanych tutaj informacji.

W tym samouczku pokazano, jak utworzyć aplikację wiersza polecenia platformy .NET korzystającą z biblioteki System.CommandLine. Zaczniesz od utworzenia prostego polecenia głównego, które ma jedną opcję. Następnie utworzysz na tej podstawie bardziej złożoną aplikację zawierającą wiele poleceń podrzędnych i różne opcje dla każdego polecenia.

Z tego samouczka dowiesz się, jak wykonywać następujące działania:

  • Utwórz polecenia, opcje i argumenty.
  • Określ wartości domyślne opcji.
  • Przypisz opcje i argumenty do poleceń.
  • Przypisz opcję rekursywnie do wszystkich podpoleceń w poleceniu.
  • Pracować z wieloma poziomami zagnieżdżonych poleceń podrzędnych.
  • Utwórz aliasy dla poleceń i opcji.
  • Praca z typami string, string[], int, bool, FileInfo i typami wyliczeniowymi.
  • Odczytywanie wartości opcji w kodzie działania polecenia.
  • Użyj niestandardowego kodu do analizowania i sprawdzania poprawności opcji.

Wymagania wstępne

  • Najnowsza wersja zestawu .NET SDK
  • Edytor programu Visual Studio Code
  • Zestaw deweloperski C#

lub

Tworzenie aplikacji

Utwórz projekt aplikacji konsolowej platformy .NET 9 o nazwie "scl".

  1. Utwórz folder o nazwie scl dla projektu, a następnie otwórz wiersz polecenia w nowym folderze.

  2. Uruchom następujące polecenie:

    dotnet new console --framework net9.0
    

Instalowanie pakietu System.CommandLine

  • Uruchom następujące polecenie:

    dotnet add package System.CommandLine --prerelease
    

    Lub w .NET 10+:

    dotnet package add System.CommandLine --prerelease
    

    Opcja --prerelease jest niezbędna, ponieważ biblioteka jest nadal dostępna w wersji beta.

Analizowanie argumentów

  1. Zastąp zawartość pliku Program.cs następującym kodem:

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

Poprzedni kod:

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);
  • Analizuje args i sprawdza, czy dla opcji --file podano jakąkolwiek wartość. Jeśli tak, wywołuje metodę ReadFile przy użyciu przeanalizowanej wartości i zwraca 0 kod zakończenia:
ParseResult parseResult = rootCommand.Parse(args);
if (parseResult.GetValue(fileOption) is FileInfo parsedFile)
{
    ReadFile(parsedFile);
    return 0;
}
  • Jeśli dla parametru nie podano --fileżadnej wartości , wyświetla dostępne błędy analizy i zwraca 1 kod zakończenia:
foreach (ParseError parseError in parseResult.Errors)
{
    Console.Error.WriteLine(parseError.Message);
}
return 1;
  • Metoda ReadFile odczytuje określony plik i wyświetla jego zawartość w konsoli:
static void ReadFile(FileInfo file)
{
    foreach (string line in File.ReadLines(file.FullName))
    {
        Console.WriteLine(line);
    }
}

Testowanie aplikacji

Możesz użyć dowolnego z następujących sposobów testowania podczas tworzenia aplikacji wiersza polecenia:

  • dotnet build Uruchom polecenie, a następnie otwórz wiersz polecenia w folderze scl/bin/Debug/net9.0, aby uruchomić plik wykonywalny:

    dotnet build
    cd bin/Debug/net9.0
    scl --file scl.runtimeconfig.json
    
  • Użyj dotnet run i przekaż wartości opcji do aplikacji zamiast polecenia run, dołączając je po --, jak w poniższym przykładzie:

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

W tym samouczku założono, że używasz pierwszej z tych opcji.

Po uruchomieniu aplikacji wyświetla zawartość pliku określonego przez opcję --file.

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

Ale co się stanie, jeśli poprosisz go o wyświetlenie pomocy, podając --help? Nic nie jest wyświetlane na konsoli, ponieważ aplikacja nie obsługuje jeszcze scenariusza, w którym --file nie zostało dostarczone i nie ma żadnych błędów parsowania.

Przeanalizuj argumenty i wywołaj element ParseResult

System.CommandLine Umożliwia określenie akcji wywoływanej, gdy dany symbol (polecenie, dyrektywa lub opcja) jest analizowany pomyślnie. Akcja to delegat, który przyjmuje System.CommandLine.ParseResult parametr i zwraca int kod zakończenia ( dostępne są również akcje asynchroniczne). Kod zakończenia jest zwracany przez metodę System.CommandLine.Parsing.ParseResult.Invoke i może służyć do wskazania, czy polecenie zostało wykonane pomyślnie, czy nie.

  1. Zastąp zawartość pliku Program.cs następującym kodem:

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

Poprzedni kod:

  • Określa, że ReadFile jest to metoda, która będzie wywoływana po wywołaniu głównego polecenia:

    rootCommand.SetAction(parseResult =>
    {
        FileInfo parsedFile = parseResult.GetValue(fileOption);
        ReadFile(parsedFile);
        return 0;
    });
    
  • Analizuje element args i wywołuje wynik:

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

Po uruchomieniu aplikacji wyświetla zawartość pliku określonego przez opcję --file.

Co się stanie, jeśli poprosisz go o wyświetlenie pomocy, podając --help?

scl --help

Zostanie wypisany następujący wynik:

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 domyślnie udostępnia opcję Pomoc, opcję Wersji i dyrektywę Sugerowanie. ParseResult.Invoke Metoda jest odpowiedzialna za wywoływanie akcji przeanalizowanego symbolu. Może to być akcja jawnie zdefiniowana dla naszego polecenia lub akcja pomocy zdefiniowana przez System.CommandLine dla System.CommandLine.Help.HelpOption. Ponadto, gdy wykryje błędy analizy, drukuje je do standardowego błędu, drukuje pomoc dla standardowych danych wyjściowych i zwraca 1 kod zakończenia:

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

Dodawanie podpolecenia i opcji

W tej sekcji Ty:

  • Utwórz więcej opcji.
  • Utwórz podpolecenie.
  • Przypisz nowe opcje do nowego podpolecenia.

Nowe opcje umożliwiają skonfigurowanie kolorów tekstu pierwszego planu i tła oraz szybkości odczytu. Te funkcjonalności będą używane do odczytywania kolekcji cytatów, które pochodzą z samouczka aplikacji konsolowej Teleprompter .

  1. Skopiuj plik sampleQuotes.txt z repozytorium GitHub dla tego przykładu do katalogu projektu. Aby uzyskać informacje na temat pobierania plików, zobacz instrukcje w Przykłady i samouczki.

  2. Otwórz plik projektu i dodaj element <ItemGroup> tuż przed tagiem zamykającym </Project>:

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

    Dodanie tego znacznika powoduje skopiowanie pliku tekstowego do folderu bin/debug/net9.0 podczas kompilowania aplikacji. Dlatego po uruchomieniu pliku wykonywalnego w tym folderze można uzyskać dostęp do pliku według nazwy bez określania ścieżki folderu.

  3. W Program.cspo kodzie tworzącym opcję --file utwórz opcje kontrolowania szybkości odczytu i kolorów tekstu:

    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. Po wierszu, który tworzy polecenie główne, usuń kod, który dodaje opcję --file do niego. Usuwasz go tutaj, ponieważ dodasz go do nowego podpolecenia.

  5. Po wierszu, który tworzy główne polecenie, utwórz podpolecenie read. Dodaj opcje do tego podpolecenia (przy użyciu składni inicjatora kolekcji zamiast właściwości Options) i dodaj podpolecenie do głównego polecenia.

    Command readCommand = new("read", "Read and display the file.")
    {
        fileOption,
        delayOption,
        fgcolorOption,
        lightModeOption
    };
    rootCommand.Subcommands.Add(readCommand);
    
  6. Zastąp kod SetAction następującym kodem SetAction dla nowego podpolecenia:

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

    Nie wywołujesz SetAction już polecenia głównego, ponieważ polecenie główne nie wymaga już akcji. Gdy polecenie ma podpolecenia, zazwyczaj należy określić jedno z podpolecenia podczas wywoływania aplikacji wiersza polecenia.

  7. Zastąp metodę ReadFile akcji następującym kodem:

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

Aplikacja wygląda teraz następująco:

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

Testowanie nowego podpolecenia

Teraz, jeśli spróbujesz uruchomić aplikację bez określenia podpolecenia, zostanie wyświetlony komunikat o błędzie z komunikatem pomocy określającym dostępne podpolecenia.

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.

Tekst pomocy dla podpolecenia read wskazuje, że dostępne są cztery opcje. Wyświetla prawidłowe wartości enumeracji.

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

Uruchom polecenie podrzędne read określając tylko opcję --file i uzyskasz wartości domyślne pozostałych trzech opcji.

scl read --file sampleQuotes.txt

Domyślne opóźnienie 42 milisekund na znak powoduje spowolnienie szybkości odczytu. Możesz go przyspieszyć, ustawiając --delay na mniejszą liczbę.

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

Aby ustawić kolory tekstu, możesz użyć --fgcolor i --light-mode:

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

Podaj nieprawidłową wartość dla --delay i zostanie wyświetlony komunikat o błędzie:

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

Podaj nieprawidłową wartość dla --file i otrzymasz wyjątek:

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''

Dodawanie podpoleceń i walidacji niestandardowych

W tej sekcji zostanie utworzona ostateczna wersja aplikacji. Po zakończeniu aplikacja będzie mieć następujące polecenia i opcje:

  • polecenie główne z opcją rekursywną* o nazwie --file
    • polecenie quotes
      • read polecenie z opcjami o nazwie --delay, --fgcolori --light-mode
      • add polecenie z argumentami o nazwach quote i byline
      • delete komenda z opcją o nazwie --search-terms

* Opcja rekursywna jest dostępna dla polecenia, do którego jest przypisana, i rekursywnie do wszystkich jego podpoleceń.

Oto przykładowe dane wejściowe wiersza polecenia, które wywołują każde z dostępnych poleceń z jego opcjami i argumentami:

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. W Program.cszastąp kod, który tworzy opcję --file następującym kodem:

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

    Ten kod używa System.CommandLine.Parsing.ArgumentResult w celu zapewnienia niestandardowej analizy, walidacji i obsługi błędów.

    Bez tego kodu brakujące pliki są raportowane przy pomocy wyjątków i śladu stosu. Po wyświetleniu tego kodu zostanie wyświetlony tylko określony komunikat o błędzie.

    Ten kod określa również wartość domyślną, dlatego ustawia DefaultValueFactory na niestandardową metodę analizowania.

  2. Po kodzie tworzącym lightModeOptiondodaj opcje i argumenty dla poleceń add i delete:

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

    Ustawienie xref:System.CommandLine.Option.AllowMultipleArgumentsPerToken pozwala pominąć nazwę opcji --search-terms podczas określania elementów na liście po pierwszym. Przykłady danych wejściowych wiersza polecenia, które są równoważne, to:

    scl quotes delete --search-terms David "You can do"
    scl quotes delete --search-terms David --search-terms "You can do"
    
  3. Zastąp kod, który tworzy polecenie główne i polecenie read następującym kodem:

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

    Ten kod wprowadza następujące zmiany:

    • Usuwa opcję --file z polecenia read.

    • Dodaje opcję --file jako opcję rekursywną do polecenia głównego.

    • Tworzy polecenie quotes i dodaje je do głównego polecenia.

    • Dodaje polecenie read do polecenia quotes zamiast do polecenia głównego.

    • Tworzy polecenia add i delete i dodaje je do polecenia quotes.

    Wynikiem jest następująca hierarchia poleceń:

    • Polecenie główne
      • quotes
        • read
        • add
        • delete

    Aplikacja implementuje teraz zalecany wzorzec, w którym polecenie nadrzędne (quotes) określa obszar lub grupę, a jego polecenia podrzędne (read, add, delete) są akcjami.

    Opcje cykliczne są stosowane do polecenia i cyklicznie do podpolecenia. Ponieważ --file jest w poleceniu głównym, będzie on automatycznie dostępny we wszystkich podpoleceniach aplikacji.

  4. Po kodzie SetAction dodaj nowy kod SetAction dla nowych poleceń podrzędnych:

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

    Podpolecenie quotes nie ma akcji, ponieważ nie jest to polecenie końcowe. Podpolecenia read, addi delete są poleceniami końcowymi w quotes, a SetAction jest wywoływane dla każdego z nich.

  5. Dodaj akcje dla add i 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}");
    }
    

Zakończona aplikacja wygląda następująco:

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

Skompiluj projekt, a następnie spróbuj wykonać następujące polecenia.

Prześlij nieistniejący plik do --file przy użyciu polecenia read, a zamiast wyjątku i śledzenia stosu otrzymasz komunikat o błędzie:

scl quotes read --file nofile
File does not exist

Spróbuj uruchomić polecenie podrzędne quotes i zostanie wyświetlony komunikat kierujący cię do używania read, addlub delete:

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.

Uruchom polecenie podrzędne add, a następnie przyjrzyj się końcu pliku tekstowego, aby zobaczyć dodany tekst:

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

Uruchom polecenie podrzędne delete z ciągami wyszukiwania od początku pliku, a następnie przyjrzyj się początku pliku tekstowego, aby zobaczyć, gdzie został usunięty tekst:

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

Uwaga

Jeśli uruchamiasz plik w folderze bin/debug/net9.0, tam znajdziesz plik ze zmianami z poleceń add i delete. Kopia pliku w folderze projektu pozostaje niezmieniona.

Następne kroki

Podczas tego samouczka stworzyłeś prostą aplikację wiersza polecenia, która korzysta z System.CommandLine. Aby dowiedzieć się więcej o bibliotece, zobacz System.CommandLine omówienie.