{"id":204,"date":"2021-02-03T10:52:01","date_gmt":"2021-02-03T18:52:01","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/pax-windows\/?p=204"},"modified":"2021-02-09T17:04:25","modified_gmt":"2021-02-10T01:04:25","slug":"command-line-parser-on-net5","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/ifdef-windows\/command-line-parser-on-net5\/","title":{"rendered":"Command Line Parser on .NET5"},"content":{"rendered":"<h2>Command Line Apps<\/h2>\n<p>Today we are going to show you how to get started parsing Command Line arguments, following on from our series on <code>.NET 5<\/code>.<\/p>\n<p>Command Line Applications, also known as Console Applications, are programs built to be used from a shell, such as cmd or bash. They have been around since the 1960&#8217;s, way before Windows, MacOS, or any other graphical user interface (GUI) came to be.<\/p>\n<p>Usually, when you start learning a programing language, the simplest and most common sample is widely known as a <code>Hello world<\/code> app. These samples pretty much only print the text &#8220;Hello world&#8221; on the console, using their built-in APIs. Computer software can do a lot of different things. Sometimes you will have an input which is, somehow, translated to an output. Our <code>Hello world<\/code> sample doesn&#8217;t have any input.<\/p>\n<p>Lets take <code>C#\/.Net<\/code>. Whenever you create a new console app, you start with a <code>Program.cs<\/code> file with a static <code>Main<\/code> method that is the entry point of your application:<\/p>\n<pre><code class=\"csharp\">...\r\nstatic void Main(string[] args)\r\n{\r\n    Console.WriteLine(\"Hello World!\");\r\n}\r\n...\r\n<\/code><\/pre>\n<p>A very important part of this code is the <code>string[] args<\/code> definition. That is the parameter definition that contains all the arguments that are passed down to our executable during the initialization of our process. Unlike C and C++, the name of the program is not treated as the first command-line argument in the args array. If you want that value, you can call the <a href=\"https:\/\/docs.microsoft.com\/dotnet\/api\/system.environment.getcommandlineargs\"><code>Environment.GetCommandLineArgs()<\/code><\/a>.<\/p>\n<p>If you are used to command-line apps, passing arguments to other apps is a very common task. Yes, you can manually parse those values, but once you have multiple parameters it can be a very error-prone code (which is mostly boilerplate anyway). This seems like a problem that someone else might have fixed already, right? So of course we can find a <a href=\"https:\/\/www.nuget.org\/\">NuGet<\/a> library that helps us parse these arguments. For this blog post, I&#8217;ll focus on <a href=\"https:\/\/www.nuget.org\/packages\/CommandLineParser\">CommandLineParser<\/a>.<\/p>\n<hr \/>\n<h2>CommandLineParser<\/h2>\n<p>The <code>CommandLineParser<\/code> is an <a href=\"https:\/\/github.com\/commandlineparser\/commandline\">open-source library<\/a> built by <a href=\"https:\/\/github.com\/ericnewton76\">Eric Newton<\/a> and members of the .NET community. It&#8217;s been around since 2005 and it has more than <strong>26 million downloads<\/strong>! The <code><a href=\"https:\/\/github.com\/commandlineparser\/commandline#command-line-parser-library-for-clr-and-netstandard\">CommandLineParser<\/a><\/code> <em>&#8220;offers CLR applications a clean and concise API for manipulating command line arguments and related tasks, such as defining switches, options and verb commands&#8221;<\/em>.<\/p>\n<p>Instead of manually parsing the args string array, you can simply define a class that will be parsed for you by the library, based on a set of attributes that you annotate the class with.<\/p>\n<p>Instead of creating yet another sample just for the sake of showing this library, I&#8217;ll use the WinML .NET5 console app that I&#8217;ve shared on my <a href=\"https:\/\/aka.ms\/winml-net5\">last blog post<\/a>. Here is the <a href=\"https:\/\/github.com\/azchohfi\/ImageClassifierSample\/tree\/net5cmd\">source code<\/a>. Lets start with it and add the <a href=\"https:\/\/www.nuget.org\/packages\/CommandLineParser\"><code>CommandLineParser<\/code> NuGet package<\/a>:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/pax-windows\/wp-content\/uploads\/sites\/61\/2021\/01\/Net5Cmd-NuGet.png\" alt=\"Adding the CommandLineParser NuGet package\" \/><\/p>\n<p>Lets create a new class named <code>CommandLineOptions<\/code>:<\/p>\n<pre><code class=\"csharp\">using CommandLine;\r\n\r\nnamespace ImageClassifier\r\n{\r\n    public class CommandLineOptions\r\n    {\r\n        [Value(index: 0, Required = true, HelpText = \"Image file Path to analyze.\")]\r\n        public string Path { get; set; }\r\n\r\n        [Option(shortName: 'c', longName: \"confidence\", Required = false, HelpText = \"Minimum confidence.\", Default = 0.9f)]\r\n        public float Confidence { get; set; }\r\n    }\r\n}\r\n<\/code><\/pre>\n<p>This is pretty much everything we need to use this library. The <code>ValueAttribute<\/code> and <code>OptionAttribute<\/code> are both provided by the package. I&#8217;m using named parameters to make it very clear what each argument is for. Back to our <code>Program.cs<\/code>&#8216; <code>Main<\/code> method, lets add the <code>using<\/code> statement to be able to easily use the package&#8217;s classes in this file:<\/p>\n<pre><code class=\"csharp\">using CommandLine;\r\n<\/code><\/pre>\n<p>Lets change the return type of our <code>Main<\/code> method to be a <code>Task&lt;int&gt;<\/code>. It means that whichever <code>int<\/code> value we return will be returned to the caller of our process, which usually indicates success\/failure. In this example, we&#8217;ll simply return <code>0<\/code> on success and something other than <code>0<\/code> on errors:<\/p>\n<pre><code class=\"csharp\">static async Task&lt;int&gt; Main(string[] args)\r\n{\r\n    return await Parser.Default.ParseArguments&lt;CommandLineOptions&gt;(args)\r\n        .MapResult(async (CommandLineOptions opts) =&gt;\r\n        {\r\n            try\r\n            {\r\n                \/\/ We have the parsed arguments, so let's just pass them down\r\n                return await AnalyzeFileAsync(opts.Path, opts.Confidence);\r\n            }\r\n            catch\r\n            {\r\n                Console.WriteLine(\"Error!\");\r\n                return -3; \/\/ Unhandled error\r\n            }\r\n        },\r\n        errs =&gt; Task.FromResult(-1)); \/\/ Invalid arguments\r\n}\r\n<\/code><\/pre>\n<p>You can see all the changes from the previous version of the code <a href=\"https:\/\/github.com\/azchohfi\/ImageClassifierSample\/compare\/net5winml...net5cmd\">here<\/a>.<\/p>\n<p>With these changes in place the app gracefully parses our arguments. There is even a help page generated automatically for us!<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/pax-windows\/wp-content\/uploads\/sites\/61\/2021\/01\/Net5Cmd-Help.png\" alt=\"Automatic help creation\" \/><\/p>\n<p>So lets say you want to analyze an image, but you want its result even if you are not too sure about it, lets say a confidence of 30%. That can easily be done now using the <code>-c<\/code> (of the long name <code>--confidence<\/code>) argument. With this image:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/pax-windows\/wp-content\/uploads\/sites\/61\/2021\/01\/Net5Cmd-NotALion.jpg\" alt=\"Not a lion\" \/><\/p>\n<p>You can get this result when using the <code>--confidence<\/code>:<\/p>\n<pre><code>&gt; .ImageClassifier.exe C:\\Users\\alzollin\\Downloads\\NotALion.jpg --confidence 0.3\r\nImage 'C:\\Users\\alzollin\\Downloads\\NotALion.jpg' is classified as 'Persian cat'(p=58%).\r\n<\/code><\/pre>\n<p>Simple, right?<\/p>\n<h2>Conclusion<\/h2>\n<p>The <code>CommandLineParser<\/code> NuGet package is a very powerful helper that simplifies this common repetitive task into a simple declarative approach. Also, it is much more customizable than what I&#8217;ve demonstrated here. You can find it&#8217;s documentation at their <a href=\"https:\/\/github.com\/commandlineparser\/commandline\/wiki\">GitHub&#8217;s Wiki page<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Lets learn how to parse command-line arguments in a C#\/.NET5 application, and how a simple NuGet package can make this process much easier and declarative.<\/p>\n","protected":false},"author":40006,"featured_media":208,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[21,22,24,10,18,23,8],"class_list":["post-204","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-ifdef-windows","tag-cmd","tag-commandline","tag-commandlineparser","tag-desktop","tag-net5","tag-parser","tag-windows"],"acf":[],"blog_post_summary":"<p>Lets learn how to parse command-line arguments in a C#\/.NET5 application, and how a simple NuGet package can make this process much easier and declarative.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/ifdef-windows\/wp-json\/wp\/v2\/posts\/204","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/ifdef-windows\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/ifdef-windows\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ifdef-windows\/wp-json\/wp\/v2\/users\/40006"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ifdef-windows\/wp-json\/wp\/v2\/comments?post=204"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/ifdef-windows\/wp-json\/wp\/v2\/posts\/204\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ifdef-windows\/wp-json\/wp\/v2\/media\/208"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/ifdef-windows\/wp-json\/wp\/v2\/media?parent=204"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ifdef-windows\/wp-json\/wp\/v2\/categories?post=204"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ifdef-windows\/wp-json\/wp\/v2\/tags?post=204"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}