Dela via


Självstudie: Kom igång med System.CommandLine

Viktigt

System.CommandLine är för närvarande i förhandsversion, och den här dokumentationen är för version 2.0 beta 4. Viss information gäller förhandsversionsprodukt som kan ändras avsevärt innan den släpps. Microsoft lämnar inga garantier, uttryckliga eller underförstådda, avseende informationen som visas här.

Den här självstudien visar hur du skapar en .NET-kommandoradsapp som använder System.CommandLine biblioteket. Du börjar med att skapa ett enkelt rotkommando som har ett alternativ. Sedan lägger du till i den basen och skapar en mer komplex app som innehåller flera underkommandon och olika alternativ för varje kommando.

I den här guiden får du lära dig att:

  • Skapa kommandon, alternativ och argument.
  • Ange standardvärden för alternativ.
  • Tilldela alternativ och argument till kommandon.
  • Tilldela ett alternativ rekursivt till alla underkommandon under ett kommando.
  • Arbeta med flera nivåer av kapslade underkommandon.
  • Skapa alias för kommandon och alternativ.
  • Arbeta med stringalternativtyperna , string[], int, boolFileInfo och uppräkning.
  • Binda alternativvärden till kommandohanterarkod.
  • Använd anpassad kod för att parsa och verifiera alternativ.

Förutsättningar

Eller

Skapa appen

Skapa ett .NET 6-konsolappprojekt med namnet "scl".

  1. Skapa en mapp med namnet scl för projektet och öppna sedan en kommandotolk i den nya mappen.

  2. Kör följande kommando:

    dotnet new console --framework net6.0
    

Installera System.CommandLine-paketet

  • Kör följande kommando:

    dotnet add package System.CommandLine --prerelease
    

    Alternativet --prerelease är nödvändigt eftersom biblioteket fortfarande är i betaversion.

  1. Ersätt innehållet i Program.cs med följande kod:

    using System.CommandLine;
    
    namespace scl;
    
    class Program
    {
        static async Task<int> Main(string[] args)
        {
            var fileOption = new Option<FileInfo?>(
                name: "--file",
                description: "The file to read and display on the console.");
    
            var rootCommand = new RootCommand("Sample app for System.CommandLine");
            rootCommand.AddOption(fileOption);
    
            rootCommand.SetHandler((file) => 
                { 
                    ReadFile(file!); 
                },
                fileOption);
    
            return await rootCommand.InvokeAsync(args);
        }
    
        static void ReadFile(FileInfo file)
        {
            File.ReadLines(file.FullName).ToList()
                .ForEach(line => Console.WriteLine(line));
        }
    }
    

Koden ovan:

  • Skapar ett alternativ med namnet --file av typen FileInfo och tilldelar det till rotkommandot:

    var fileOption = new Option<FileInfo?>(
        name: "--file",
        description: "The file to read and display on the console.");
    
    var rootCommand = new RootCommand("Sample app for System.CommandLine");
    rootCommand.AddOption(fileOption);
    
  • Anger att ReadFile är den metod som anropas när rotkommandot anropas:

    rootCommand.SetHandler((file) => 
        { 
            ReadFile(file!); 
        },
        fileOption);
    
  • Visar innehållet i den angivna filen när rotkommandot anropas:

    static void ReadFile(FileInfo file)
    {
        File.ReadLines(file.FullName).ToList()
            .ForEach(line => Console.WriteLine(line));
    }
    

Testa appen

Du kan använda något av följande sätt att testa när du utvecklar en kommandoradsapp:

  • dotnet build Kör kommandot och öppna sedan en kommandotolk i mappen scl/bin/Debug/net6.0 för att köra den körbara filen:

    dotnet build
    cd bin/Debug/net6.0
    scl --file scl.runtimeconfig.json
    
  • Använd dotnet run och skicka alternativvärden till appen i stället för till kommandot genom att run inkludera dem efter --, som i följande exempel:

    dotnet run -- --file scl.runtimeconfig.json
    

    I .NET 7.0.100 SDK Preview kan du använda commandLineArgs filen launchSettings.json genom att köra kommandot dotnet run --launch-profile <profilename>.

  • Publicera projektet i en mapp, öppna en kommandotolk till mappen och kör den körbara filen:

    dotnet publish -o publish
    cd ./publish
    scl --file scl.runtimeconfig.json
    
  • I Visual Studio 2022 väljer du Felsöka>felsökningsegenskaper på menyn och anger alternativen och argumenten i rutan Kommandoradsargument . Exempel:

    Kommandoradsargument i Visual Studio 2022

    Kör sedan appen, till exempel genom att trycka på Ctrl+F5.

Den här självstudien förutsätter att du använder det första av dessa alternativ.

När du kör appen visas innehållet i filen som anges av --file alternativet .

{
  "runtimeOptions": {
    "tfm": "net6.0",
    "framework": {
      "name": "Microsoft.NETCore.App",
      "version": "6.0.0"
    }
  }
}

Hjälputdata

System.CommandLine ger automatiskt hjälputdata:

scl --help
Description:
  Sample app for System.CommandLine

Usage:
  scl [options]

Options:
  --file <file>   The file to read and display on the console.
  --version       Show version information
  -?, -h, --help  Show help and usage information

Versionsutdata

System.CommandLine tillhandahåller automatiskt versionsutdata:

scl --version
1.0.0

Lägg till en underkommando och alternativ

I det här avsnittet får du:

  • Skapa fler alternativ.
  • Skapa en underkommando.
  • Tilldela de nya alternativen till den nya underkommandot.

Med de nya alternativen kan du konfigurera förgrunds- och bakgrundstextfärgerna och avläsningshastigheten. De här funktionerna används för att läsa en samling citattecken som kommer från självstudiekursen för Teleprompter-konsolappen.

  1. Kopiera sampleQuotes.txt-filen från GitHub-lagringsplatsen för det här exemplet till projektkatalogen. Information om hur du laddar ned filer finns i anvisningarna i Exempel och självstudier.

  2. Öppna projektfilen och lägg till ett <ItemGroup> element precis före den avslutande </Project> taggen:

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

    Om du lägger till den här pålägget kopieras textfilen till mappen bin/debug/net6.0 när du skapar appen. Så när du kör den körbara filen i mappen kan du komma åt filen efter namn utan att ange en mappsökväg.

  3. Efter koden som skapar --file alternativet i Program.cs skapar du alternativ för att styra avläsningshastigheten och textfärgerna:

    var delayOption = new Option<int>(
        name: "--delay",
        description: "Delay between lines, specified as milliseconds per character in a line.",
        getDefaultValue: () => 42);
    
    var fgcolorOption = new Option<ConsoleColor>(
        name: "--fgcolor",
        description: "Foreground color of text displayed on the console.",
        getDefaultValue: () => ConsoleColor.White);
    
    var lightModeOption = new Option<bool>(
        name: "--light-mode",
        description: "Background color of text displayed on the console: default is black, light mode is white.");
    
  4. Efter raden som skapar rotkommandot tar du bort raden som lägger till --file alternativet i det. Du tar bort den här eftersom du lägger till den i en ny underkommando.

    var rootCommand = new RootCommand("Sample app for System.CommandLine");
    //rootCommand.AddOption(fileOption);
    
  5. Efter raden som skapar rotkommandot skapar du en read underkommando. Lägg till alternativen i den här underkommandot och lägg till underkommandot i rotkommandot.

    var readCommand = new Command("read", "Read and display the file.")
        {
            fileOption,
            delayOption,
            fgcolorOption,
            lightModeOption
        };
    rootCommand.AddCommand(readCommand);
    
  6. SetHandler Ersätt koden med följande SetHandler kod för den nya underkommandot:

    readCommand.SetHandler(async (file, delay, fgcolor, lightMode) =>
        {
            await ReadFile(file!, delay, fgcolor, lightMode);
        },
        fileOption, delayOption, fgcolorOption, lightModeOption);
    

    Du anropar SetHandler inte längre rotkommandot eftersom rotkommandot inte längre behöver en hanterare. När ett kommando har underkommandon måste du vanligtvis ange en av underkommandona när du anropar en kommandoradsapp.

  7. ReadFile Ersätt hanteringsmetoden med följande kod:

    internal static async Task ReadFile(
            FileInfo file, int delay, ConsoleColor fgColor, bool lightMode)
    {
        Console.BackgroundColor = lightMode ? ConsoleColor.White : ConsoleColor.Black;
        Console.ForegroundColor = fgColor;
        List<string> lines = File.ReadLines(file.FullName).ToList();
        foreach (string line in lines)
        {
            Console.WriteLine(line);
            await Task.Delay(delay * line.Length);
        };
    }
    

Appen ser nu ut så här:

using System.CommandLine;

namespace scl;

class Program
{
    static async Task<int> Main(string[] args)
    {
        var fileOption = new Option<FileInfo?>(
            name: "--file",
            description: "The file to read and display on the console.");

        var delayOption = new Option<int>(
            name: "--delay",
            description: "Delay between lines, specified as milliseconds per character in a line.",
            getDefaultValue: () => 42);

        var fgcolorOption = new Option<ConsoleColor>(
            name: "--fgcolor",
            description: "Foreground color of text displayed on the console.",
            getDefaultValue: () => ConsoleColor.White);

        var lightModeOption = new Option<bool>(
            name: "--light-mode",
            description: "Background color of text displayed on the console: default is black, light mode is white.");

        var rootCommand = new RootCommand("Sample app for System.CommandLine");
        //rootCommand.AddOption(fileOption);

        var readCommand = new Command("read", "Read and display the file.")
            {
                fileOption,
                delayOption,
                fgcolorOption,
                lightModeOption
            };
        rootCommand.AddCommand(readCommand);

        readCommand.SetHandler(async (file, delay, fgcolor, lightMode) =>
            {
                await ReadFile(file!, delay, fgcolor, lightMode);
            },
            fileOption, delayOption, fgcolorOption, lightModeOption);

        return rootCommand.InvokeAsync(args).Result;
    }

    internal static async Task ReadFile(
            FileInfo file, int delay, ConsoleColor fgColor, bool lightMode)
    {
        Console.BackgroundColor = lightMode ? ConsoleColor.White : ConsoleColor.Black;
        Console.ForegroundColor = fgColor;
        List<string> lines = File.ReadLines(file.FullName).ToList();
        foreach (string line in lines)
        {
            Console.WriteLine(line);
            await Task.Delay(delay * line.Length);
        };
    }
}

Testa den nya underkommandot

Om du nu försöker köra appen utan att ange underkommandot får du ett felmeddelande följt av ett hjälpmeddelande som anger den underkommando som är tillgänglig.

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:
  --version       Show version information
  -?, -h, --help  Show help and usage information

Commands:
  read  Read and display the file.

Hjälptexten för underkommandot read visar att fyra alternativ är tillgängliga. Den visar giltiga värden för uppräkningen.

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

Kör underkommandot read och ange endast --file alternativet och du får standardvärdena för de andra tre alternativen.

scl read --file sampleQuotes.txt

Standardfördröjningen på 42 millisekunder per tecken orsakar en långsam avläsningshastighet. Du kan påskynda det genom att ange --delay ett lägre tal.

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

Du kan använda --fgcolor och --light-mode för att ange textfärger:

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

Ange ett ogiltigt värde för --delay och du får ett felmeddelande:

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

Ange ett ogiltigt värde för --file och du får ett undantag:

scl read --file nofile
Unhandled exception: System.IO.FileNotFoundException:
Could not find file 'C:\bin\Debug\net6.0\nofile'.

Lägga till underkommandon och anpassad validering

Det här avsnittet skapar den slutliga versionen av appen. När appen är klar har den följande kommandon och alternativ:

  • rotkommando med ett alternativ för global* med namnet --file
    • Kommandot quotes
      • read kommandot med alternativen --delay, --fgcoloroch --light-mode
      • add kommando med argument med namnet quote och byline
      • delete kommando med alternativet med namnet --search-terms

* Ett globalt alternativ är tillgängligt för kommandot som det tilldelas till och rekursivt till alla dess underkommandon.

Här är exempel på kommandoradsindata som anropar vart och ett av de tillgängliga kommandona med dess alternativ och argument:

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. I Program.cs ersätter du koden som skapar --file alternativet med följande kod:

    var fileOption = new Option<FileInfo?>(
        name: "--file",
        description: "An option whose argument is parsed as a FileInfo",
        isDefault: true,
        parseArgument: result =>
        {
            if (result.Tokens.Count == 0)
            {
                return new FileInfo("sampleQuotes.txt");
    
            }
            string? filePath = result.Tokens.Single().Value;
            if (!File.Exists(filePath))
            {
                result.ErrorMessage = "File does not exist";
                return null;
            }
            else
            {
                return new FileInfo(filePath);
            }
        });
    

    Den här koden används ParseArgument<T> för att tillhandahålla anpassad parsning, validering och felhantering.

    Utan den här koden rapporteras saknade filer med ett undantags- och stackspårning. Med den här koden visas bara det angivna felmeddelandet.

    Den här koden anger också ett standardvärde, vilket är anledningen till att den anger isDefault till true. Om du inte anger isDefault till trueanropas inte ombudet parseArgument när inga indata anges för --file.

  2. Efter koden som skapar lightModeOptionlägger du till alternativ och argument för kommandona add och delete :

    var searchTermsOption = new Option<string[]>(
        name: "--search-terms",
        description: "Strings to search for when deleting entries.")
        { IsRequired = true, AllowMultipleArgumentsPerToken = true };
    
    var quoteArgument = new Argument<string>(
        name: "quote",
        description: "Text of quote.");
    
    var bylineArgument = new Argument<string>(
        name: "byline",
        description: "Byline of quote.");
    

    Med AllowMultipleArgumentsPerToken inställningen kan du utelämna --search-terms alternativnamnet när du anger element i listan efter det första. Det gör följande exempel på motsvarande kommandoradsindata:

    scl quotes delete --search-terms David "You can do"
    scl quotes delete --search-terms David --search-terms "You can do"
    
  3. Ersätt koden som skapar rotkommandot och read kommandot med följande kod:

    var rootCommand = new RootCommand("Sample app for System.CommandLine");
    rootCommand.AddGlobalOption(fileOption);
    
    var quotesCommand = new Command("quotes", "Work with a file that contains quotes.");
    rootCommand.AddCommand(quotesCommand);
    
    var readCommand = new Command("read", "Read and display the file.")
        {
            delayOption,
            fgcolorOption,
            lightModeOption
        };
    quotesCommand.AddCommand(readCommand);
    
    var deleteCommand = new Command("delete", "Delete lines from the file.");
    deleteCommand.AddOption(searchTermsOption);
    quotesCommand.AddCommand(deleteCommand);
    
    var addCommand = new Command("add", "Add an entry to the file.");
    addCommand.AddArgument(quoteArgument);
    addCommand.AddArgument(bylineArgument);
    addCommand.AddAlias("insert");
    quotesCommand.AddCommand(addCommand);
    

    Den här koden gör följande ändringar:

    • Tar --file bort alternativet från read kommandot .

    • --file Lägger till alternativet som ett globalt alternativ i rotkommandot.

    • Skapar ett quotes kommando och lägger till det i rotkommandot.

    • read Lägger till kommandot i quotes kommandot i stället för till rotkommandot.

    • Skapar add och delete kommandon och lägger till dem i quotes kommandot .

    Resultatet är följande kommandohierarki:

    • Rotkommando
      • quotes
        • read
        • add
        • delete

    Appen implementerar nu det rekommenderade mönstret där det överordnade kommandot (quotes) anger ett område eller en grupp och dess underordnade kommandon (read, add, delete) är åtgärder.

    Globala alternativ tillämpas på kommandot och rekursivt på underkommandon. Eftersom --file finns i rotkommandot blir det automatiskt tillgängligt i alla underkommandon i appen.

  4. SetHandler Efter koden lägger du till ny SetHandler kod för de nya underkommandona:

    deleteCommand.SetHandler((file, searchTerms) =>
        {
            DeleteFromFile(file!, searchTerms);
        },
        fileOption, searchTermsOption);
    
    addCommand.SetHandler((file, quote, byline) =>
        {
            AddToFile(file!, quote, byline);
        },
        fileOption, quoteArgument, bylineArgument);
    

    Underkommandot quotes har ingen hanterare eftersom det inte är ett lövkommando. Underkommandon read, addoch delete är lövkommandon under quotesoch anropas för var och SetHandler en av dem.

  5. Lägg till hanterare för add och delete.

    internal static void DeleteFromFile(FileInfo file, string[] searchTerms)
    {
        Console.WriteLine("Deleting from file");
        File.WriteAllLines(
            file.FullName, File.ReadLines(file.FullName)
                .Where(line => searchTerms.All(s => !line.Contains(s))).ToList());
    }
    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}");
        writer.Flush();
    }
    

Den färdiga appen ser ut så här:

using System.CommandLine;

namespace scl;

class Program
{
    static async Task<int> Main(string[] args)
    {
        var fileOption = new Option<FileInfo?>(
            name: "--file",
            description: "An option whose argument is parsed as a FileInfo",
            isDefault: true,
            parseArgument: result =>
            {
                if (result.Tokens.Count == 0)
                {
                    return new FileInfo("sampleQuotes.txt");

                }
                string? filePath = result.Tokens.Single().Value;
                if (!File.Exists(filePath))
                {
                    result.ErrorMessage = "File does not exist";
                    return null;
                }
                else
                {
                    return new FileInfo(filePath);
                }
            });

        var delayOption = new Option<int>(
            name: "--delay",
            description: "Delay between lines, specified as milliseconds per character in a line.",
            getDefaultValue: () => 42);

        var fgcolorOption = new Option<ConsoleColor>(
            name: "--fgcolor",
            description: "Foreground color of text displayed on the console.",
            getDefaultValue: () => ConsoleColor.White);

        var lightModeOption = new Option<bool>(
            name: "--light-mode",
            description: "Background color of text displayed on the console: default is black, light mode is white.");

        var searchTermsOption = new Option<string[]>(
            name: "--search-terms",
            description: "Strings to search for when deleting entries.")
            { IsRequired = true, AllowMultipleArgumentsPerToken = true };

        var quoteArgument = new Argument<string>(
            name: "quote",
            description: "Text of quote.");

        var bylineArgument = new Argument<string>(
            name: "byline",
            description: "Byline of quote.");

        var rootCommand = new RootCommand("Sample app for System.CommandLine");
        rootCommand.AddGlobalOption(fileOption);

        var quotesCommand = new Command("quotes", "Work with a file that contains quotes.");
        rootCommand.AddCommand(quotesCommand);

        var readCommand = new Command("read", "Read and display the file.")
            {
                delayOption,
                fgcolorOption,
                lightModeOption
            };
        quotesCommand.AddCommand(readCommand);

        var deleteCommand = new Command("delete", "Delete lines from the file.");
        deleteCommand.AddOption(searchTermsOption);
        quotesCommand.AddCommand(deleteCommand);

        var addCommand = new Command("add", "Add an entry to the file.");
        addCommand.AddArgument(quoteArgument);
        addCommand.AddArgument(bylineArgument);
        addCommand.AddAlias("insert");
        quotesCommand.AddCommand(addCommand);

        readCommand.SetHandler(async (file, delay, fgcolor, lightMode) =>
            {
                await ReadFile(file!, delay, fgcolor, lightMode);
            },
            fileOption, delayOption, fgcolorOption, lightModeOption);

        deleteCommand.SetHandler((file, searchTerms) =>
            {
                DeleteFromFile(file!, searchTerms);
            },
            fileOption, searchTermsOption);

        addCommand.SetHandler((file, quote, byline) =>
            {
                AddToFile(file!, quote, byline);
            },
            fileOption, quoteArgument, bylineArgument);

        return await rootCommand.InvokeAsync(args);
    }

    internal static async Task ReadFile(
                FileInfo file, int delay, ConsoleColor fgColor, bool lightMode)
    {
        Console.BackgroundColor = lightMode ? ConsoleColor.White : ConsoleColor.Black;
        Console.ForegroundColor = fgColor;
        var lines = File.ReadLines(file.FullName).ToList();
        foreach (string line in lines)
        {
            Console.WriteLine(line);
            await Task.Delay(delay * line.Length);
        };

    }
    internal static void DeleteFromFile(FileInfo file, string[] searchTerms)
    {
        Console.WriteLine("Deleting from file");
        File.WriteAllLines(
            file.FullName, File.ReadLines(file.FullName)
                .Where(line => searchTerms.All(s => !line.Contains(s))).ToList());
    }
    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}");
        writer.Flush();
    }
}

Skapa projektet och prova sedan följande kommandon.

Skicka en obefintlig fil till --file med read kommandot så får du ett felmeddelande i stället för ett undantag och stackspårning:

scl quotes read --file nofile
File does not exist

Försök att köra underkommandot quotes så får du ett meddelande som uppmanar dig att använda read, addeller 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.

Kör underkommandot addoch titta sedan i slutet av textfilen för att se den tillagda texten:

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

Kör underkommandot delete med söksträngar från början av filen och titta sedan i början av textfilen för att se var texten togs bort:

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

Anteckning

Om du kör i mappen bin/debug/net6.0 hittar du filen med ändringar från add kommandona och delete i den mappen. Kopian av filen i projektmappen förblir oförändrad.

Nästa steg

I den här självstudien skapade du en enkel kommandoradsapp som använder System.CommandLine. Mer information om biblioteket finns i System.CommandLine översikten.