Partilhar via


Tutorial: Comece com System.CommandLine

Importante

System.CommandLine está atualmente em pré-visualização e esta documentação destina-se à versão 2.0 beta 5. Algumas informações estão relacionadas ao produto de pré-lançamento que pode ser substancialmente modificado antes de ser lançado. A Microsoft não oferece garantias, expressas ou implícitas, em relação às informações fornecidas aqui.

Este tutorial mostra como criar um aplicativo de linha de comando .NET que usa a System.CommandLine biblioteca. Você começará criando um comando root simples que tem uma opção. Em seguida, você criará essa base, criando um aplicativo mais complexo que contém vários subcomandos e opções diferentes para cada comando.

Neste tutorial, você aprenderá a:

  • Crie comandos, opções e argumentos.
  • Especifique valores padrão para opções.
  • Atribua opções e argumentos a comandos.
  • Atribua uma opção recursivamente a todos os subcomandos sob um comando.
  • Trabalhe com vários níveis de subcomandos encadeados.
  • Crie aliases para comandos e opções.
  • Trabalhe com string, string[], int, bool, FileInfo e com tipos de opções enum.
  • Leia os valores da opção no código de ação do comando.
  • Use código personalizado para analisar e validar opções.

Pré-requisitos

Ou

Criar a aplicação

Crie um projeto de aplicativo de console do .NET 9 chamado "scl".

  1. Crie uma pasta chamada scl para o projeto e, em seguida, abra um prompt de comando na nova pasta.

  2. Execute o seguinte comando:

    dotnet new console --framework net9.0
    

Instalar o pacote System.CommandLine

  • Execute o seguinte comando:

    dotnet add package System.CommandLine --prerelease
    

    Ou, no .NET 10+:

    dotnet package add System.CommandLine --prerelease
    

    A --prerelease opção é necessária porque a biblioteca ainda está em versão beta.

Analise os argumentos

  1. Substitua o conteúdo do ficheiro Program.cs pelo seguinte código:

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

O código anterior:

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);
  • Analisa o args e verifica se algum valor foi fornecido para a opção --file. Em caso afirmativo, ele chama o método ReadFile usando o valor analisado e retorna o código de saída 0.
ParseResult parseResult = rootCommand.Parse(args);
if (parseResult.GetValue(fileOption) is FileInfo parsedFile)
{
    ReadFile(parsedFile);
    return 0;
}
  • Se nenhum valor foi fornecido para --file, ele imprime erros de análise disponíveis e retorna 1 o código de saída:
foreach (ParseError parseError in parseResult.Errors)
{
    Console.Error.WriteLine(parseError.Message);
}
return 1;
  • O ReadFile método lê o arquivo especificado e exibe seu conteúdo no console:
static void ReadFile(FileInfo file)
{
    foreach (string line in File.ReadLines(file.FullName))
    {
        Console.WriteLine(line);
    }
}

Testar a aplicação

Você pode usar qualquer uma das seguintes maneiras de testar durante o desenvolvimento de um aplicativo de linha de comando:

  • Execute o dotnet build comando e, em seguida, abra um prompt de comando na pasta scl/bin/Debug/net9.0 para executar o executável:

    dotnet build
    cd bin/Debug/net9.0
    scl --file scl.runtimeconfig.json
    
  • Use dotnet run e passe valores de opção para o aplicativo em vez de para o comando, run incluindo-os após --, como no exemplo a seguir:

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

Este tutorial pressupõe que você esteja usando a primeira dessas opções.

Quando você executa o aplicativo, ele exibe o conteúdo do arquivo especificado pela --file opção.

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

Mas o que acontece se você pedir que ele exiba a ajuda fornecendo --help? Nada é impresso no console, porque o aplicativo ainda não lida com o cenário onde --file não é fornecido e não há erros de análise.

Analise os argumentos e invoque o ParseResult

System.CommandLine Permite especificar uma ação que é invocada quando um determinado símbolo (comando, diretiva ou opção) é analisado com êxito. A ação é um delegado que aceita um System.CommandLine.ParseResult parâmetro e devolve um código de int saída (ações assíncronas também estão disponíveis). O código de saída é retornado pelo System.CommandLine.Parsing.ParseResult.Invoke método e pode ser usado para indicar se o comando foi executado com êxito ou não.

  1. Substitua o conteúdo do ficheiro Program.cs pelo seguinte código:

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

O código anterior:

  • Especifica que o método ReadFile será chamado quando o comando raiz for invocado:

    rootCommand.SetAction(parseResult =>
    {
        FileInfo parsedFile = parseResult.GetValue(fileOption);
        ReadFile(parsedFile);
        return 0;
    });
    
  • Analisa o args e invoca o resultado:

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

Quando você executa o aplicativo, ele exibe o conteúdo do arquivo especificado pela --file opção.

O que acontece se você pedir que ele exiba a ajuda fornecendo --help?

scl --help

O seguinte resultado é impresso:

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 por padrão, fornece a opção Ajuda, a opção Versão e a diretiva Sugestão. ParseResult.Invoke método é responsável por invocar a ação do símbolo analisado. Pode ser a ação explicitamente definida para o nosso comando, ou a ação de ajuda definida por System.CommandLine for System.CommandLine.Help.HelpOption. Além disso, quando deteta erros de análise, imprime-os no erro padrão, imprime ajuda à saída padrão e devolve 1 o código de saída:

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

Adicionar um subcomando e opções

Nesta secção, pode:

  • Crie mais opções.
  • Crie um subcomando.
  • Atribua as novas opções ao novo subcomando.

As novas opções permitirão configurar as cores do texto em primeiro plano e plano de fundo e a velocidade de leitura. Esses recursos serão usados para ler uma coleção de citações que vem do tutorial do aplicativo de console Teleprompter.

  1. Copie o arquivo sampleQuotes.txt do repositório GitHub para este exemplo no diretório do projeto. Para obter informações sobre como baixar arquivos, consulte as instruções em Exemplos e tutoriais.

  2. Abra o arquivo de projeto e adicione um <ItemGroup> elemento imediatamente antes da tag de fechamento </Project> :

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

    Adicionar essa marcação faz com que o arquivo de texto seja copiado para a pasta bin/debug/net9.0 quando você cria o aplicativo. Assim, quando você executa o executável nessa pasta, você pode acessar o arquivo pelo nome sem especificar um caminho de pasta.

  3. No Program.cs, após o código que cria a opção, crie opções para controlar a velocidade de leitura e as cores do --file texto:

    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. Após a linha que cria o comando raiz, exclua o código que adiciona a --file opção a ele. Você está removendo-o aqui para adicioná-lo a um novo subcomando.

  5. Após a linha que cria o comando raiz, crie um read subcomando. Adicione as opções a este subcomando (usando a sintaxe do inicializador de coleção em vez da Options propriedade) e adicione o subcomando ao comando raiz.

    Command readCommand = new("read", "Read and display the file.")
    {
        fileOption,
        delayOption,
        fgcolorOption,
        lightModeOption
    };
    rootCommand.Subcommands.Add(readCommand);
    
  6. Substitua o SetAction código pelo seguinte SetAction código para o novo subcomando:

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

    Você já não está a chamar o comando raiz SetAction porque o comando raiz já não precisa de uma ação. Quando um comando tem subcomandos, normalmente você precisa especificar um dos subcomandos ao invocar um aplicativo de linha de comando.

  7. Substitua o ReadFile método de ação pelo seguinte código:

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

O aplicativo agora tem esta aparência:

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

Testar o novo subcomando

Agora, se você tentar executar o aplicativo sem especificar o subcomando, receberá uma mensagem de erro seguida por uma mensagem de ajuda que especifica o subcomando disponível.

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.

O texto de ajuda para o subcomando read mostra que quatro opções estão disponíveis. Ele mostra valores válidos para o enum.

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

Execute o subcomando read especificando apenas a --file opção e você obterá os valores padrão para as outras três opções.

scl read --file sampleQuotes.txt

O atraso padrão de 42 milissegundos por caractere causa uma velocidade de leitura lenta. Você pode acelerá-lo definindo --delay para um número mais baixo.

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

Você pode usar --fgcolor e --light-mode definir cores de texto:

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

Forneça um valor inválido para --delay e você receberá uma mensagem de erro:

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

Forneça um valor inválido para --file e você obtém uma exceção:

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

Adicionar subcomandos e validação personalizada

Esta seção cria a versão final do aplicativo. Quando terminar, o aplicativo terá os seguintes comandos e opções:

  • comando root com uma opção recursiva* chamada --file
    • quotes comando
      • read com opções denominadas --delay, --fgcolor, e --light-mode
      • add comando com argumentos nomeados quote e byline
      • delete comando com opção nomeada --search-terms

* Uma opção recursiva está disponível para o comando ao qual é atribuído e recursivamente para todos os seus subcomandos.

Aqui está um exemplo de entrada de linha de comando que invoca cada um dos comandos disponíveis com suas opções e argumentos:

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. No Program.cs, substitua o código que cria a --file opção pelo seguinte código:

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

    Esse código é usado System.CommandLine.Parsing.ArgumentResult para fornecer análise, validação e tratamento de erros personalizados.

    Sem esse código, os arquivos ausentes são relatados com uma exceção e rastreamento de pilha. Com esse código, apenas a mensagem de erro especificada é exibida.

    Esse código também especifica um valor padrão, e é por isso que ele define DefaultValueFactory como método de análise personalizado.

  2. Após o código que cria lightModeOption, adicione opções e argumentos para os add comandos e 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."
    };
    

    A configuração xref:System.CommandLine.Option.AllowMultipleArgumentsPerToken permite omitir o nome da opção --search-terms ao especificar elementos na lista, após o primeiro. Ele torna equivalentes os seguintes exemplos de entrada de linha de comando:

    scl quotes delete --search-terms David "You can do"
    scl quotes delete --search-terms David --search-terms "You can do"
    
  3. Substitua o código que cria o comando raiz e o read comando pelo seguinte código:

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

    Este código faz as seguintes alterações:

    • Remover a opção --file do comando read.

    • Adiciona a --file opção como uma opção recursiva ao comando root.

    • Cria um quotes comando e o adiciona ao comando raiz.

    • Adiciona o read comando ao quotes comando em vez de ao comando raiz.

    • Cria add e delete comanda e adiciona-os ao quotes comando.

    O resultado é a seguinte hierarquia de comandos:

    • Comando raiz
      • quotes
        • read
        • add
        • delete

    O aplicativo agora implementa o padrão recomendado onde o comando pai (quotes) especifica uma área ou grupo, e seus comandos filhos (read, add, delete) são ações.

    As opções recursivas são aplicadas ao comando e recursivamente aos subcomandos. Como --file está no comando root, ele estará disponível automaticamente em todos os subcomandos do aplicativo.

  4. Após o SetAction código, adicione novo SetAction código para os novos subcomandos:

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

    O subcomando quotes não tem uma ação porque não é um comando folha. Subcomandos read, add, e delete são comandos folha em quotes, e SetAction é chamado para cada um deles.

  5. Adicione as ações para add e 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}");
    }
    

O aplicativo concluído tem esta aparência:

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

Crie o projeto e, em seguida, tente os seguintes comandos.

Submete um ficheiro inexistente para --file usando o comando read e receberás uma mensagem de erro em vez de uma exceção e rastreio de pilha:

scl quotes read --file nofile
File does not exist

Tente executar o subcomando quotes e você receberá uma mensagem orientando você a usar read, add, ou 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.

Execute o subcomando adde, em seguida, olhe para o final do arquivo de texto para ver o texto adicionado:

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

Execute o subcomando delete com cadeias de caracteres de pesquisa desde o início do arquivo e, em seguida, examine o início do arquivo de texto para ver onde o texto foi removido:

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

Observação

Se estiveres a executar na pasta bin/debug/net9.0, é nessa pasta que encontrarás o ficheiro com as alterações dos comandos add e delete. A cópia do arquivo na pasta do projeto permanece inalterada.

Próximos passos

Neste tutorial, criaste uma aplicação de linha de comando simples que utiliza System.CommandLine. Para saber mais sobre a biblioteca, consulte System.CommandLine Visão geral.