dotnet / command-line-api

Command line parsing, invocation, and rendering of terminal output.
https://github.com/dotnet/command-line-api/wiki
MIT License
3.37k stars 378 forks source link

My "Thank You" offering to you #1982

Open dagilleland opened 1 year ago

dagilleland commented 1 year ago

I'm loving the System.CommandLine package, and I've teaching it for the first time in my Programming Fundamentals class (post-secondary). Thank You!

I'm building up a good tutorial for absolute beginners to use. Here's a PDF of one of my documents that I thought I would share with you (and the community).

Specs-Day-3.pdf

p.s. - If you like my work and think it can be helpful on Microsoft Learn or some other place, I'm quite happy to share. Just let me know what you would need for me to contribute. Thanks again!

dagilleland commented 1 year ago

And here's an embedded version of what I wrote (for those who prefer not to download the PDF above). Note that some links won't work (obviously) because it is currently part of a template repository I'm using for my students.


Setting up your Command-Line Application

Estimated time: 1 hour

System.CommandLine was created to help developers build their own command-line applications. The primary goal was to handle all the "messy" parts of a typical CLI so that the developer can focus on the "real" purpose of their application. Specifically, the creators of the System.CommandLine had these motivations.

  • Provide a parser that's independent of binding and invocation.
  • Make the end user command line experience consistent and inclusive.
  • A composable chain of responsibility for subcommand routing, middleware, directives, etc.
  • Rich, adaptive output rendering
  • Other things:

As such, their high-level goals for "creating great command line experiences" for developers include

Getting Started

As a new developer, you should have a basic understanding of C# programming (i.e.: variables, methods, flow-control statements, and simple OOP) as well as these specific concepts:

Terminology

There are three core terms to understand when it comes to CLI applications.

A Simple Example

In the vein of the traditional "Hello World"™ application, the following code sample represents a command-line application that has an option to display the author of the program. Modify your Program.cs so that it contains this code.

NOTE: For best results, you should manually type the following code into your Program.cs rather than just copying and pasting it. Remember, your primary goal here is to understand what the code is doing. Typing it out yourself (including all the comments) will slow down your thinking enough to help you figure out how to leverage the System.CommandLine for your own purposes in the future.

using System.CommandLine;
using System.CommandLine.Builder;
using System.CommandLine.Parsing;

internal class Program
{
    private static void Main(string[] args)
    {
        // 1) Define the Options, Arguments, and SubCommands that your CLI will support
        var authorOption = new Option<bool>("--author", "Display information about who created this program.");
        //                           ^----^ ^--------^  ^--- description of the option (for --help) --------^
        //                              |      |
        //                              |      |_ The name of the option (i.e.: the "flag" send into our CLI app)
        //                              |_ The data type this option will be converted to.
        //                                   - will return a true if "--author" is passed in and false if it is absent

        // 2) Create a root command that represents the whole application (subcommands and all)
        var rootCommand = new RootCommand("Sample CLI using System.CommandLine");
        //                                ^--- description of this program ---^

        // Add your option to your root command
        rootCommand.AddOption(authorOption);

        // Identify what method should be called in order to handle/process the root command
        rootCommand.SetHandler<bool>(RunMainCommand, authorOption);
        //                    ^----^ ^------------^  ^----------^
        //                       |         |             |_ The Option<T> that will be converted into a T value.
        //                       |         |                  - The converted value will be passed into RunMainCommand
        //                       |         |_ The name of the method (delegate/action) that should be called
        //                       |            by System.CommandLine when it's done parsing the string[] args
        //                       |_ The data type of the parameter for the RunMainCommand method.

        // 3) Use the CommandLineBuilder to include additional configuration for your CLI app.
        //    Notice how the CommandLineBuilder provides a "fluent interface" for setting up
        var myCommandLineApp = new CommandLineBuilder(rootCommand)
            .UseDefaults()  // This represents the configuration settings most commonly desired for CLI apps
            .Build();       // This final call generates the "parser" object that will process/interpret the args

        // 4) Run (invoke) your application by sending in the string[] args data from the Main method
        myCommandLineApp.Invoke(args);
    }

    private static void RunMainCommand(bool showAuthorInformation)
    {
        if(showAuthorInformation)
        {
            Console.WriteLine("Username:  stewdent");       // Your GitHub username
            Console.WriteLine("Full name: Stewart Dent");   // Your full name
        }
        else
        {
            Console.WriteLine("Try re-running this application with the --help option");
        }
    }
}

Conclusion

Test your understanding of what you have learned so far by completing the following.

Q&A

Copy the following questions into your docs\ReadMe.md file along with your observations of how your program currently works.

  1. What happens when you run your program by entering dotnet run in the terminal?
  2. What is the difference between typing dotnet run --help and dotnet run -- --help?
    1. What is the meaning/purpose of the extra -- in the command?
  3. What happens when you run your program by entering dotnet run -- --author?
    1. Does it have a different result than typing dotnet run --author? Why or why not?
  4. What is the result of entering in dotnet run -- --version in the terminal?
    1. Is it the same as entering dotnet run --version? Why or why not?
  5. What is the result of entering dotnet run bob in the terminal? Is it what you expected would happen?

Customizing Your Program

There is some background information to your application that is detailed in your .csproj file (see NaitCli.csproj). This file is critical to your application because it tells the compiler important information to use when generating your executable (.exe) program.

Modify your program by adding a <Version>0.1.0</Version> tag in its <PropertyGroup> tag. Your final contents should match the following (ignoring any version difference in the included "System.CommandLine" package reference).

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net6.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
    <Version>0.1.0</Version>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
  </ItemGroup>

</Project>

Now, re-run your application in the terminal by typing dotnet run -- --version. Note the difference output than what you got before this change. Make some notes in your docs\ReadMe.md to describe what you have done.