Condividi tramite


Esercitazione: Inizia con System.CommandLine

Importante

System.CommandLine è attualmente disponibile in ANTEPRIMA e questa documentazione è per la versione 2.0 beta 5. Alcune informazioni riguardano il prodotto in fase di pre-rilascio che potrebbe essere modificato in modo sostanziale prima del rilascio. Microsoft non fornisce alcuna garanzia, espressa o implicita, in relazione alle informazioni fornite qui.

Questa esercitazione illustra come creare un'app da riga di comando .NET che usa la System.CommandLine libreria. Si inizierà creando un semplice comando radice con un'unica opzione. Successivamente costruirai su quella base, creando un'app più complessa con più sottocomandi e opzioni diverse per ogni comando.

In questa esercitazione si apprenderà come:

  • Creare comandi, opzioni e argomenti.
  • Specificare i valori predefiniti per le opzioni.
  • Assegnare opzioni e argomenti ai comandi.
  • Assegnare un'opzione in modo ricorsivo a tutti i sottocomandi in un comando.
  • Lavorare con più livelli di sottocomandi annidati.
  • Creare alias per comandi e opzioni.
  • Lavora con string, string[], int, bool, FileInfo e tipi di opzioni di enumerazione.
  • Leggere i valori delle opzioni nel codice azione del comando.
  • Usare codice personalizzato per l'analisi e la convalida delle opzioni.

Prerequisiti

  • La versione più recente .NET SDK
  • editor di Visual Studio Code
  • Il DevKit C#

oppure

Creare l'app

Creare un progetto di app console .NET 9 denominato "scl".

  1. Creare una cartella denominata scl per il progetto e quindi aprire un prompt dei comandi nella nuova cartella.

  2. Eseguire il comando seguente:

    dotnet new console --framework net9.0
    

Installare il pacchetto System.CommandLine

  • Eseguire il comando seguente:

    dotnet add package System.CommandLine --prerelease
    

    In alternativa, in .NET 10+:

    dotnet package add System.CommandLine --prerelease
    

    L'opzione --prerelease è necessaria perché la libreria è ancora in versione beta.

Analizzare gli argomenti

  1. Sostituire il contenuto di Program.cs con il codice seguente:

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

Il codice precedente:

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);
  • Analizza args e controlla se è stato specificato un valore per l'opzione --file. In tal caso, chiama il metodo ReadFile usando il valore 0 elaborato e restituisce il codice di uscita:
ParseResult parseResult = rootCommand.Parse(args);
if (parseResult.GetValue(fileOption) is FileInfo parsedFile)
{
    ReadFile(parsedFile);
    return 0;
}
  • Se non è stato specificato alcun valore per --file, stampa gli errori di analisi disponibili e restituisce 1 il codice di uscita:
foreach (ParseError parseError in parseResult.Errors)
{
    Console.Error.WriteLine(parseError.Message);
}
return 1;
  • Il ReadFile metodo legge il file specificato e ne visualizza il contenuto nella console:
static void ReadFile(FileInfo file)
{
    foreach (string line in File.ReadLines(file.FullName))
    {
        Console.WriteLine(line);
    }
}

Testare l'app

È possibile usare uno dei modi seguenti per testare durante lo sviluppo di un'app da riga di comando:

  • Eseguire il dotnet build comando e quindi aprire un prompt dei comandi nella cartella scl/bin/Debug/net9.0 per eseguire il file eseguibile:

    dotnet build
    cd bin/Debug/net9.0
    scl --file scl.runtimeconfig.json
    
  • Usare dotnet run e passare i valori delle opzioni all'app anziché al run comando includendoli dopo --, come nell'esempio seguente:

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

Questa esercitazione presuppone che si stia usando la prima di queste opzioni.

Quando si esegue l'app, viene visualizzato il contenuto del file specificato dall'opzione --file .

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

Ma cosa succede se si chiede di visualizzare l'aiuto fornendo --help? Nulla viene stampato nella console, perché l'app non gestisce ancora lo scenario in cui --file non viene fornito e non sono presenti errori di analisi.

Analizzare gli argomenti e richiamare ParseResult

System.CommandLine consente di specificare un'azione richiamata quando un determinato simbolo (comando, direttiva o opzione) viene analizzato correttamente. L'azione è un delegato che accetta un parametro e restituisce un System.CommandLine.ParseResultint codice di uscita (sono disponibili anche azioni asincrone). Il codice di uscita viene restituito dal System.CommandLine.Parsing.ParseResult.Invoke metodo e può essere usato per indicare se il comando è stato eseguito correttamente o meno.

  1. Sostituire il contenuto di Program.cs con il codice seguente:

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

Il codice precedente:

  • Specifica che ReadFile è il metodo che verrà chiamato quando viene richiamato il comando radice:

    rootCommand.SetAction(parseResult =>
    {
        FileInfo parsedFile = parseResult.GetValue(fileOption);
        ReadFile(parsedFile);
        return 0;
    });
    
  • args Analizza e richiama il risultato:

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

Quando si esegue l'app, viene visualizzato il contenuto del file specificato dall'opzione --file .

Cosa accade se chiedi di visualizzare la Guida usando --help?

scl --help

L'output seguente viene stampato:

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 per impostazione predefinita, fornisce l'opzione Guida, l'opzione Versione e la direttiva Suggerisci. ParseResult.Invoke il metodo è responsabile della chiamata dell'azione del simbolo analizzato. Potrebbe trattarsi dell'azione definita esplicitamente per il nostro comando, o dell'azione di aiuto definita da System.CommandLine per System.CommandLine.Help.HelpOption. Inoltre, quando rileva eventuali errori di analisi, li stampa nell'errore standard, stampa l'aiuto all'output standard e restituisce il codice di uscita 1.

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

Aggiungere un sottocomando e opzioni

In questa sezione, tu:

  • Crea altre opzioni.
  • Creare un sottocomando.
  • Assegnare le nuove opzioni al nuovo sottocomando.

Le nuove opzioni consentono di configurare i colori del testo in primo piano e di sfondo e la velocità di lettura. Queste funzionalità verranno usate per leggere una collezione di citazioni provenienti dall'esercitazione sull'app console Teleprompter.

  1. Copiare il filesampleQuotes.txt dal repository GitHub per questo esempio nella directory del progetto. Per informazioni su come scaricare i file, vedere le istruzioni in Esempi ed esercitazioni.

  2. Aprire il file di progetto e aggiungere un <ItemGroup> elemento subito prima del tag di chiusura </Project> :

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

    L'aggiunta di questo markup fa sì che il file di testo venga copiato nella cartella bin/debug/net9.0 quando si compila l'app. Pertanto, quando si esegue il file eseguibile in tale cartella, è possibile accedere al file in base al nome senza specificare un percorso di cartella.

  3. In Program.cs, dopo il codice che crea l'opzione --file , creare opzioni per controllare la velocità di lettura e i colori del testo:

    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. Dopo la riga che crea il comando radice, eliminare il codice che aggiunge l'opzione --file . Lo stai rimuovendo perché lo aggiungerai in un nuovo sottocomando.

  5. Dopo la riga che crea il comando radice, creare un read sottocomando. Aggiungi le opzioni a questo sottocomando (usando la sintassi dell'inizializzatore di raccolta invece della proprietà Options) e aggiungi il sottocomando al comando principale.

    Command readCommand = new("read", "Read and display the file.")
    {
        fileOption,
        delayOption,
        fgcolorOption,
        lightModeOption
    };
    rootCommand.Subcommands.Add(readCommand);
    
  6. Sostituire il SetAction codice con il codice seguente SetAction per il nuovo sottocomando:

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

    Non si sta più chiamando SetAction sul comando radice perché il comando radice non richiede più un'azione. Quando un comando include sottocomandi, in genere è necessario specificare uno dei sottocomandi quando si richiama un'app da riga di comando.

  7. Sostituire il ReadFile metodo action con il codice seguente:

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

L'app avrà ora un aspetto simile al seguente:

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

Testare il nuovo sottocomando

Ora se si tenta di eseguire l'app senza specificare il sottocomando, viene visualizzato un messaggio di errore seguito da un messaggio della Guida che specifica il sottocomando disponibile.

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.

Il testo della Guida per sottocomando read mostra che sono disponibili quattro opzioni. Mostra valori validi per l'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

Eseguire sottocomando read specificando solo l'opzione --file e si ottengono i valori predefiniti per le altre tre opzioni.

scl read --file sampleQuotes.txt

Il ritardo predefinito di 42 millisecondi per carattere causa una velocità di lettura lenta. È possibile velocizzarla impostando --delay un numero inferiore.

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

È possibile usare --fgcolor e --light-mode per impostare i colori del testo:

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

Specificare un valore non valido per --delay e viene visualizzato un messaggio di errore:

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

Specificare un valore non valido per --file e si ottiene un'eccezione:

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

Aggiungere sottocomandi e convalida personalizzata

Questa sezione crea la versione finale dell'app. Al termine, l'app avrà i comandi e le opzioni seguenti:

  • comando root con un'opzione ricorsiva* denominata --file
    • quotes comando
      • read comando con opzioni denominate --delay, --fgcolore --light-mode
      • add comando con argomenti denominati quote e byline
      • delete comando con opzione denominata --search-terms

* Un'opzione ricorsiva è disponibile per il comando a cui è assegnata e ricorsivamente a tutti i relativi sottocomandi.

Ecco l'input della riga di comando di esempio che richiama ognuno dei comandi disponibili con le relative opzioni e argomenti:

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. In Program.cs sostituire il codice che crea l'opzione --file con il codice seguente:

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

    Questo codice usa System.CommandLine.Parsing.ArgumentResult per fornire analisi, convalida e gestione degli errori personalizzati.

    Senza questo codice, i file mancanti vengono segnalati con un'eccezione e un'analisi dello stack. Con questo codice viene visualizzato solo il messaggio di errore specificato.

    Questo codice specifica anche un valore predefinito, motivo per cui imposta DefaultValueFactory il metodo di analisi personalizzato.

  2. Dopo il codice che crea lightModeOption, aggiungere opzioni e argomenti per i add comandi 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."
    };
    

    L'impostazione xref:System.CommandLine.Option.AllowMultipleArgumentsPerToken consente di omettere il nome dell'opzione --search-terms quando si specificano elementi nell'elenco dopo il primo. Rende equivalenti i seguenti esempi di input da riga di comando.

    scl quotes delete --search-terms David "You can do"
    scl quotes delete --search-terms David --search-terms "You can do"
    
  3. Sostituire il codice che crea il comando radice e il read comando con il codice seguente:

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

    Questo codice apporta le modifiche seguenti:

    • Rimuove l'opzione --file dal read comando .

    • Aggiunge l'opzione --file come opzione ricorsiva al comando radice.

    • Crea un quotes comando e lo aggiunge al comando radice.

    • Aggiunge il read comando al quotes comando anziché al comando radice.

    • Crea add e delete comandi e li aggiunge al quotes comando.

    Il risultato è la gerarchia di comandi seguente:

    • Comando principale
      • quotes
        • read
        • add
        • delete

    L'app implementa ora il modello consigliato in cui il comando padre (quotes) specifica un'area o un gruppo e i relativi comandi figlio (read, add, delete) sono azioni.

    Le opzioni ricorsive vengono applicate al comando e in modo ricorsivo ai sottocomandi. Poiché --file è nel comando radice, sarà disponibile automaticamente in tutti i sottocomandi dell'app.

  4. Dopo il SetAction codice, aggiungere nuovo SetAction codice per i nuovi sottocomandi:

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

    Il sottocomando quotes non ha un'azione perché non è un comando foglia. I sottocomandi read, add e delete sono comandi foglia sotto quotes, e SetAction viene chiamato per ognuno di questi sottocomandi.

  5. Aggiungere le azioni per 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}");
    }
    

L'app completata è simile alla seguente:

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

Compilare il progetto e quindi provare i comandi seguenti.

Inviare un file inesistente a --file con il read comando e viene visualizzato un messaggio di errore anziché un'eccezione e un'analisi dello stack:

scl quotes read --file nofile
File does not exist

Provare a eseguire sottocomando quotes e viene visualizzato un messaggio che indica di usare 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.

Eseguire sottocomando adde quindi esaminare la fine del file di testo per visualizzare il testo aggiunto:

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

Eseguire sottocomando delete con stringhe di ricerca dall'inizio del file e quindi esaminare l'inizio del file di testo per vedere dove è stato rimosso il testo:

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

Annotazioni

Se si esegue nella cartella bin/debug/net9.0, quella sarà la posizione in cui si troverà il file con le modifiche apportate dai comandi add e delete. La copia del file nella cartella del progetto rimane invariata.

Passaggi successivi

In questa esercitazione è stata creata una semplice app da riga di comando che usa System.CommandLine. Per altre informazioni sulla libreria, vedere System.CommandLine panoramica.