Udostępnij przez


Samouczek: wprowadzenie do System.CommandLine

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

  • Program Visual Studio z zainstalowanym środowiskiem programistycznym .NET do tworzenia aplikacji desktopowych.

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
    

    Lub w .NET 10+:

    dotnet package add System.CommandLine
    

Analizowanie argumentów

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.Errors.Count == 0 && 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);
  • Analizować args i sprawdzać, czy podano wartość dla opcji --file. Jeśli tak, wywołuje metodę ReadFile przy użyciu przeanalizowanej wartości i zwraca kod 0zakończenia :
ParseResult parseResult = rootCommand.Parse(args);
if (parseResult.Errors.Count == 0 && parseResult.GetValue(fileOption) is FileInfo parsedFile)
{
    ReadFile(parsedFile);
    return 0;
}
  • Jeśli nie podano wartości dla --file, wyświetla znalezione błędy parsowania i zwraca kod zakończenia 1.
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 wyjściowym kompilacji, 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 jeśli poprosisz go o wyświetlenie pomocy, podając --help, nic nie zostanie wydrukowane na konsolę. Dzieje się tak, ponieważ aplikacja nie obsługuje jeszcze scenariusza, w którym --file nie podano i nie ma żadnych błędów analizy.

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 parametr i zwraca kod wyjścia . (Dostępne są również akcje asynchroniczne ). Kod zakończenia jest zwracany przez metodę ParseResult.Invoke(InvocationConfiguration) i może służyć do wskazania, czy polecenie zostało wykonane pomyślnie, czy nie.

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;
    });
    
  • args Analizuje i wywołuje wynik:

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

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

Jeśli poprosisz o wyświetlenie pomocy, określając scl --help, zostaną wyświetlone następujące informacje:

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 console

RootCommand domyślnie udostępnia opcję Pomoc, opcję wersji i dyrektywę Suggest. Metoda ParseResult.Invoke(InvocationConfiguration) jest odpowiedzialna za wywoływanie akcji przeanalizowanego symbolu. Może to być akcja jawnie zdefiniowana dla polecenia lub akcja pomocy zdefiniowana przez System.CommandLine dla System.CommandLine.Help.HelpOption.

Ponadto, gdy Invoke metoda wykryje błędy analizy, wyświetla je do standardowego błędu, drukuje pomoc w standardowych danych wyjściowych i zwraca 1 jako 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 pozwalają użytkownikowi końcowego skonfigurować kolory tekstu pierwszego planu i tła oraz szybkość 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 przykładów dotnet do katalogu projektu.

    (Aby uzyskać informacje na temat pobierania plików, zobacz instrukcje w temacie 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 wyjściowego 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

Jeśli podasz nieprawidłową wartość dla --delayelementu , 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'.

Jeśli podasz nieprawidłową wartość dla --fileelementu , 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:

Command Opcje Arguments
Polecenie główne --file (rekursywne)
quotes
read --delay, --fgcolor--light-mode
add quote i byline
delete --search-terms

(Opcja rekursywna jest dostępna dla polecenia, do którego jest przypisana, i działa rekursywnie na wszystkie jego podpolecenia.)

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 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 Option<T>.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 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ń:

    Komenda główna └──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

Jeśli spróbujesz uruchomić subpolecenie quotes, zostanie wyświetlony komunikat polecający użycie read, add lub 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

Plik w folderze wyjściowym odzwierciedla zmiany wynikające 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. Aby włączyć funkcję uzupełniania poleceń, zobacz opis Uzupełnianie poleceń dla System.CommandLineelementu.