Compartir a través de


Tutorial: Introducción a System.CommandLine

Importante

System.CommandLine actualmente está en versión preliminar y esta documentación es para la versión 2.0 beta 5. Cierta información se relaciona con el producto de versión preliminar que puede modificarse sustancialmente antes de su lanzamiento. Microsoft no ofrece ninguna garantía, expresa o implícita, con respecto a la información proporcionada aquí.

En este tutorial se muestra cómo crear una aplicación de línea de comandos de .NET que usa la biblioteca de System.CommandLine. Comenzará creando un comando raíz simple que tenga una opción. A continuación, construirá sobre esa base, creando una aplicación más compleja que contiene varios subcomandos y diferentes opciones para cada comando.

En este tutorial, aprenderá a:

  • Cree comandos, opciones y argumentos.
  • Especifique los valores predeterminados para las opciones.
  • Asigne opciones y argumentos a comandos.
  • Asigne una opción de forma recursiva a todos los subcomandos en un comando.
  • Trabaja con varios niveles de subcomandos anidados.
  • Cree alias para comandos y opciones.
  • Trabaje con los tipos de opción string, string[], int, bool, FileInfo y enumeración.
  • Leer valores de opción en el código de acción del comando.
  • Use código personalizado para analizar y validar opciones.

Prerrequisitos

O bien

Creación de la aplicación

Cree un proyecto de aplicación de consola de .NET 9 denominado "scl".

  1. Crea una carpeta denominada scl para el proyecto y abre un símbolo del sistema en la nueva carpeta.

  2. Ejecute el siguiente comando:

    dotnet new console --framework net9.0
    

Instalación del paquete System.CommandLine

  • Ejecute el siguiente comando:

    dotnet add package System.CommandLine --prerelease
    

    O bien, en .NET 10+:

    dotnet package add System.CommandLine --prerelease
    

    La opción --prerelease es necesaria porque la biblioteca todavía está en versión beta.

Análisis de los argumentos

  1. Reemplace el contenido de Program.cs por el código siguiente:

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

El 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);
  • Analiza args y comprueba si se proporcionó algún valor para la opción --file. Si es así, llama al ReadFile método mediante el valor analizado y devuelve 0 el código de salida:
ParseResult parseResult = rootCommand.Parse(args);
if (parseResult.GetValue(fileOption) is FileInfo parsedFile)
{
    ReadFile(parsedFile);
    return 0;
}
  • Si no se proporcionó ningún valor para --file, imprime los errores de análisis disponibles y devuelve 1 el código de salida:
foreach (ParseError parseError in parseResult.Errors)
{
    Console.Error.WriteLine(parseError.Message);
}
return 1;
  • El ReadFile método lee el archivo especificado y muestra su contenido en la consola:
static void ReadFile(FileInfo file)
{
    foreach (string line in File.ReadLines(file.FullName))
    {
        Console.WriteLine(line);
    }
}

Pruebas de la aplicación

Puede usar cualquiera de las siguientes maneras de probar al desarrollar una aplicación de línea de comandos:

  • Ejecute el comando dotnet build, y luego abra una línea de comandos en la carpeta scl/bin/Debug/net9.0, para ejecutar el programa.

    dotnet build
    cd bin/Debug/net9.0
    scl --file scl.runtimeconfig.json
    
  • Use dotnet run y pase valores de opción a la aplicación en lugar del comando run incluyéndolas después de --, como en el ejemplo siguiente:

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

En este tutorial se supone que usa la primera de estas opciones.

Al ejecutar la aplicación, muestra el contenido del archivo especificado por la opción --file.

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

Pero, ¿qué ocurre si se le pide que muestre la ayuda proporcionando --help? No se imprime nada en la consola porque la aplicación aún no controla el escenario en el que --file no se proporciona y no hay errores de análisis.

Análisis de los argumentos e invocación de ParseResult

System.CommandLine permite especificar una acción que se invoca cuando se analiza correctamente un símbolo determinado (comando, directiva o opción). La acción es un delegado que toma un System.CommandLine.ParseResult parámetro y devuelve un int código de salida (las acciones asincrónicas también están disponibles). El método devuelve el System.CommandLine.Parsing.ParseResult.Invoke código de salida y se puede usar para indicar si el comando se ejecutó correctamente o no.

  1. Reemplace el contenido de Program.cs por el código siguiente:

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

El código anterior:

  • Especifica que ReadFile es el método al que se llamará cuando se invoque el comando raíz:

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

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

Al ejecutar la aplicación, muestra el contenido del archivo especificado por la opción --file.

¿Qué ocurre si se le pide que muestre la ayuda proporcionando --help?

scl --help

La salida siguiente se imprime:

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 De forma predeterminada, proporciona la opción ayuda, la opción Versión y la directiva Suggest. ParseResult.Invoke el método es responsable de invocar la acción del símbolo analizado. Podría ser la acción definida explícitamente para nuestro comando o la acción de ayuda definida por System.CommandLine para System.CommandLine.Help.HelpOption. Además, cuando detecta algún error de análisis, los imprime en el error estándar, muestra ayuda en la salida estándar y devuelve el código de salida 1.

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

Agregar un subcomando y opciones

En esta sección, usted:

  • Cree más opciones.
  • Cree un subcomando.
  • Asigne las nuevas opciones al nuevo subcomando.

Las nuevas opciones le permitirán configurar los colores de texto en primer plano y de fondo y la velocidad de lectura. Estas funciones se usarán para leer una colección de citas que proceden del tutorial de la aplicación de consola Teleprompter .

  1. Copie el archivo sampleQuotes.txt del repositorio de GitHub para este ejemplo en el directorio del proyecto. Para obtener información sobre cómo descargar archivos, consulte las instrucciones de Ejemplos y tutoriales.

  2. Abra el archivo del proyecto y agregue un elemento <ItemGroup> justo antes de la etiqueta de cierre </Project>:

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

    Al agregar este marcado, el archivo de texto se copiará en la carpeta bin/debug/net9.0 al compilar la aplicación. Por lo tanto, al ejecutar el archivo ejecutable en esa carpeta, puede acceder al archivo por nombre sin especificar una ruta de acceso de carpeta.

  3. En Program.cs, después del código que crea la opción --file, cree opciones para controlar la velocidad de lectura y los colores de 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. Después de la línea que crea el comando raíz, elimine el código que le agrega la --file opción. Lo estás quitando aquí porque lo agregarás a un nuevo subcomando.

  5. Después de la línea que crea el comando raíz, cree un read subcomando. Agregue las opciones a este subcomando (mediante la sintaxis del inicializador de colección en lugar de la propiedad Options). Luego, agregue el subcomando al comando raíz.

    Command readCommand = new("read", "Read and display the file.")
    {
        fileOption,
        delayOption,
        fgcolorOption,
        lightModeOption
    };
    rootCommand.Subcommands.Add(readCommand);
    
  6. Reemplace el código SetAction por el siguiente código de SetAction para el nuevo subcomando:

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

    Ya no está llamando a SetAction en el comando raíz porque el comando raíz ya no necesita una acción. Cuando un comando tiene subcomandos, normalmente debe especificar uno de los subcomandos al invocar una aplicación de línea de comandos.

  7. Reemplace el ReadFile método de acción por el código siguiente:

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

La aplicación ahora tiene el siguiente aspecto:

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

Probar el nuevo subcomando

Ahora, si intenta ejecutar la aplicación sin especificar el subcomando, recibirá un mensaje de error seguido de un mensaje de ayuda que especifica el subcomando que está disponible.

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.

El texto de ayuda de subcomando read muestra que hay cuatro opciones disponibles. Muestra valores válidos para la enumeración.

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

Ejecute el subcomando read especificar solo la opción --file y obtendrá los valores predeterminados para las otras tres opciones.

scl read --file sampleQuotes.txt

El retraso predeterminado de 42 milisegundos por carácter provoca una velocidad de lectura lenta. Puede acelerarlo estableciendo --delay en un número inferior.

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

Puede usar --fgcolor y --light-mode para establecer colores de texto:

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

Proporcione un valor no válido para --delay y reciba un mensaje de error:

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

Proporcione un valor no válido para --file y obtenga una excepción:

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

Adición de subcomandos y validación personalizada

En esta sección se crea la versión final de la aplicación. Cuando termine, la aplicación tendrá los siguientes comandos y opciones:

  • comando raíz con una opción recursiva* denominada --file
    • comando quotes
      • read comando con opciones denominadas --delay, --fgcolory --light-mode
      • add comando con argumentos denominados quote y byline
      • comando delete con la opción denominada --search-terms

* Hay disponible una opción recursiva para el comando al que se asigna y de forma recursiva a todos sus subcomandos.

Esta es la entrada de la línea de comandos de ejemplo que invoca cada uno de los comandos disponibles con sus opciones y 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. En Program.cs, reemplace el código que crea la opción --file por el código siguiente:

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

    Este código usa System.CommandLine.Parsing.ArgumentResult para proporcionar análisis, validación y control de errores personalizados.

    Sin este código, los archivos que faltan se notifican con una excepción y un seguimiento de pila. Con este código solo se muestra el mensaje de error especificado.

    Este código también especifica un valor predeterminado, por lo que establece DefaultValueFactory en el método de análisis personalizado.

  2. Después del código que crea lightModeOption, agregue opciones y argumentos para los comandos add y 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."
    };
    

    El valor de xref:System.CommandLine.Option.AllowMultipleArgumentsPerToken permite omitir el nombre de la opción --search-terms al especificar elementos de la lista después del primero. Convierte los siguientes ejemplos de entrada de línea de comandos en equivalentes:

    scl quotes delete --search-terms David "You can do"
    scl quotes delete --search-terms David --search-terms "You can do"
    
  3. Reemplace el código que crea el comando raíz y el comando read por el código siguiente:

    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 realiza los siguientes cambios:

    • Quita la opción --file del comando read.

    • Agrega la --file opción como opción recursiva al comando raíz.

    • Crea un comando quotes y lo agrega al comando raíz.

    • Agrega el comando read al comando quotes en lugar del comando raíz.

    • Crea comandos add y delete y los agrega al comando quotes.

    El resultado es la siguiente jerarquía de comandos:

    • Comando raíz
      • quotes
        • read
        • add
        • delete

    La aplicación ahora implementa el patrón recomendado donde el comando primario (quotes) especifica un área o grupo, y sus comandos secundarios (read, add, delete) son acciones.

    Las opciones recursivas se aplican al comando y de forma recursiva a los subcomandos. Dado que --file está en el comando raíz, estará disponible automáticamente en todos los subcomandos de la aplicación.

  4. Después del código de SetAction, agregue el nuevo código SetAction para los nuevos subcomandos:

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

    El subcomando quotes no tiene una acción porque no es un comando hoja. Los subcomandos read, addy delete son comandos hoja en quotesy SetAction se llama a para cada uno de ellos.

  5. Agregue las acciones de add y 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}");
    }
    

La aplicación finalizada tiene el siguiente aspecto:

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

Compile el proyecto y pruebe los siguientes comandos.

Envíe un archivo inexistente a --file con el comando read y recibirá un mensaje de error en lugar de una excepción y un seguimiento de pila:

scl quotes read --file nofile
File does not exist

Intente ejecutar quotes de subcomando y obtenga un mensaje que le dirija a usar read, addo 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.

Ejecute el subcomando addy, a continuación, examine el final del archivo de texto para ver el texto agregado:

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

Ejecute el subcomando delete con cadenas de búsqueda desde el principio del archivo y, a continuación, examine el principio del archivo de texto para ver dónde se quitó el texto:

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

Nota:

Si se ejecuta en la carpeta bin/debug/net9.0, encontrará en esa carpeta el archivo con cambios de los comandos add y delete. La copia del archivo en la carpeta del proyecto permanece sin cambios.

Pasos siguientes

En este tutorial, ha creado una aplicación de línea de comandos sencilla que usa System.CommandLine. Para obtener más información sobre la biblioteca, consulte System.CommandLine información general.