System.CommandLine parsed values don't match input - c#

I am trying to use System.CommandLine and I haven't been able to get my handler to see any of the values that I'm passing in. I've tried the simplest command line program just to see if any values make it through and so far I haven't been successful. I am targeting .NET 4.7.2 and I'm using System.CommandLine 2.0.0-beta1.20574.7
using System;
using System.CommandLine;
using System.CommandLine.Invocation;
static class Program
{
public static void Main(string[] args)
{
var rootCommand = new RootCommand
{
new Option("--continue", "continue option")
};
rootCommand.Description = "Testing System.CommandLine";
rootCommand.Handler = CommandHandler.Create<bool>
((willContinue) => run(willContinue));
rootCommand.Invoke(args);
}
private static void run(bool willContinue)
{
Console.WriteLine(willContinue);
}
}
No matter how I call my application, I am not seeing the value of willContinue come across as true.
myapp.exe --continue -> False
myapp.exe --cont -> Unrecognized command or argument '--cont' (my options are at least getting recognized)
myapp.exe --continue true -> Unrecognized command or argument 'true'
myapp.exe --help ->
myapp:
Testing System.CommandLine
Usage:
myapp [options]
Options:
--continue continue option
--version Show version information
-?, -h, --help Show help and usage information

You need to fix 2 things:
add the option type, which is bool
change the name of the option to match the parameter name
the following command works:
var rootCommand = new RootCommand
{
new Option<bool>("--willContinue", "continue option")
};
and call it like so
myapp.exe --willContinue true
the option name and parameter name don't always have to match-up, but in this case it doesn't work because 'continue' is a reserved word

I wanted to add an answer to be very clear about exactly what resolved my problem
using System;
using System.CommandLine;
using System.CommandLine.Invocation;
static class Program
{
public static void Main(string[] args)
{
var rootCommand = new RootCommand
{
new Option("--willContinue", "continue option")
// ^This option name
};
rootCommand.Description = "Testing System.CommandLine";
rootCommand.Handler = CommandHandler.Create<bool>
((WiLLCoNtInUe) => run(WiLLCoNtInUe));
// ^ HAS to match this parameter name where the command handler is created.
// but does not have to match case
rootCommand.Invoke(args);
}
private static void run(bool canBeSomethingElse)
// Because of how this is called ^ this does not have to match
{
Console.WriteLine(canBeSomethingElse);
}
}
Because the Argument/Option/Command/anything else that can be added to a Command object has to match the name of the parameter used when creating the CommandHandler, the names used can't have spaces, begin with numbers, or use a C# keyword (not having spaces is not a problem for most items because it would be silly/impossible to have an option --will continue, but with arguments you don't have to put the name of the argument into your command line call because it is listed in the usage as <argument name> and the argument is read in by position, so I was wanting to use an argument name like <file directory>, but that doesn't work because you can't have a variable name with spaces in it)

Related

How to get command line parameters in specific format?

For example, we have option "--date".
I use System.CommandLine library to get options from command line and it works with such format:
"--date 2023-02-06", but I want it to work with format kind of: "--date=2023-02-06". Is there a way to do this?
If you don't mind using a beta Microsoft library you could use System.CommandLine.
From Option-argument delimiters:
Option-argument delimiters
System.CommandLine lets you use a space, '=', or ':' as the delimiter between an option name and its argument. For example, the following commands are equivalent:
dotnet build -v quiet
dotnet build -v=quiet
dotnet build -v:quiet
For example (This is modified Tutorial: Get started with System.CommandLine):
// dotnet add package System.CommandLine --prerelease
using System.CommandLine;
internal class Program
{
static async Task<int> Main(string[] args)
{
var date = new Option<string?>(
name: "--date",
description: "TODO");
var rootCommand = new RootCommand("TODO");
rootCommand.AddOption(date);
rootCommand.SetHandler((date) =>
{
Run(date!);
},
date);
return await rootCommand.InvokeAsync(args);
}
static void Run(string date)
{
Console.WriteLine(date);
}
}
Then we can:
PS C:\git\games\bin\Release\net6.0\win10-x64\publish> .\games.exe --date 2023-02-06
2023-02-06
PS C:\git\games\bin\Release\net6.0\win10-x64\publish> .\games.exe --date:2023-02-06
2023-02-06
PS C:\git\games\bin\Release\net6.0\win10-x64\publish> .\games.exe --date=2023-02-06
2023-02-06

Creating a command line in C# that includes file path

I am trying to create a project that accepts a configuration file and 2 comparison files using a command line arguments with the paths to these files included. Would I construct this the same way you would pass any command line argument? Sorry I am new to this so I am not sure if there is an exception when trying to pass files.
Can I get an example of how this would be done? Here is a picture of the directions of what exactly I have been asked.
Accept the following command line arguments:
Configuration file (with path) (described below)
Comparison File 1 (with path)
Comparison File 2 (with path)
Take a look at the documentation of Main function arguments
Assuming this is your main function and you want to accept 3 parameters:
static int Main(string[] args)
{
// check the length of args for validation.
// args[0] -> Configuration file
// args[1] -> Comparison File 1
// args[2] -> Comparison File 2
..... DO SOMETHING...
return 0;
}
Usage (from command line or debugger):
SomeProgram.exe "ConfigFilePath" "ComparisonFile1" "ComparisonFile2".
Because I really like this nuget(No association just a fan). Here is an example of it using CommandLineUtils
First add an new project with dotnet new consol TestConsolUtils then add the nuget dotnet add package McMaster.Extensions.CommandLineUtils then copy this code to the program class.
using McMaster.Extensions.CommandLineUtils;
using System;
namespace ConsolUtilsTest
{
class Program
{
public static int Main(string[] args)
=> CommandLineApplication.Execute<Program>(args);
[Argument(0, Description = "Configuration file")]
[FileExists]
public string ConfigurationFile { get; }
[Argument(1, Description = "Comparison file 1")]
[FileExists]
public string ComparisonFile1 { get; }
[Argument(2, Description = "Comparison File 2")]
[FileExists]
public string ComparisonFile2 { get; }
private void OnExecute()
{
Console.WriteLine(ConfigurationFile);
Console.WriteLine(ComparisonFile1);
Console.WriteLine(ComparisonFile2);
}
}
}
do a dotnet build
Go to the dll folder that was just build most likely in Debug\netcoreapp2.2\
Create a fake file A.json this is required because the utility will check if the file exists.
Run it with dotnet command
dotnet TestConsolUtils.dll A.json A.json A.json
There are a lot more you can do with this utill just look at the documentation.

Implementing a command line interpreter

In terminal or cmd, you can write commands, in which there is a main command and then sub-commands, or arguments and stuff...like this:
cd Desktop\Folder
lst
Format E: /fs:FAT32
I want to create a C# console application that could execute predefined commands like this, but which could also split up main commands and sub-commands, in which some could be optional and some not. I have tried just taking all as string and then splitting it to array and creating if(s) and switch and cases, but it looks really bad and hardly manageable. I'm sure that in the OS's terminal or cmd it's build in another way. Could you help me understand the basic structure of such an application?
Here, have a look at this concept.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SharpConsole
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Welcome to SharpConsole. Type in a command.");
while (true)
{
Console.Write("$ ");
string command = Console.ReadLine();
string command_main = command.Split(new char[] { ' ' }).First();
string[] arguments = command.Split(new char[] { ' ' }).Skip(1).ToArray();
if (lCommands.ContainsKey(command_main))
{
Action<string[]> function_to_execute = null;
lCommands.TryGetValue(command_main, out function_to_execute);
function_to_execute(arguments);
}
else
Console.WriteLine("Command '" + command_main + "' not found");
}
}
private static Dictionary<string, Action<string[]>> lCommands =
new Dictionary<string, Action<string[]>>()
{
{ "help", HelpFunc },
{ "cp" , CopyFunc }
};
private static void CopyFunc(string[] obj)
{
if (obj.Length != 2) return;
Console.WriteLine("Copying " + obj[0] + " to " + obj[1]);
}
public static void HelpFunc(string[] args)
{
Console.WriteLine("===== SOME MEANINGFULL HELP ==== ");
}
}
}
The basic idea is to generalize the idea of a command. We have a Dictionary, where the key is a string (the command's name), and the value you get from the dictionary is a function of type Action<string[]>. Any function which has the signature void Function(string[]) can be used as this type. Then, you can set up this dictionary with a bunch of commands and route them to the functions you want. Each of these functions will receive an array of optional arguments. So here, the command "help" will be routed to the HelpFunc(). And the "cp" command e.g. will receive an array of filenames. The parsing of the command is always the same. We read a line, split it a space. The first string is the program's name, command_main here. If you skip the first string, you'll get an enumeration of all the other subcommands or switches you typed in. Then, a lookup in the dictionary is being done to see if there is such a command. If yes, we get the function and execute it with the arguments. If not, you should display "command not found" or something. All in all, this exercise can be minimized to looking up a function in a dictionary of possible command strings, then executing it. So a possible output is
Welcome to SharpConsole. Type in a command.
$ help
===== SOME MEANINGFULL HELP ====
$ cp file1 otherfile2
Copying file1 to otherfile2
$ python --version
Command 'python' not found
$ ...
LXSH
It's a command interpreter similar to CMD or Bash.
We've distributed it under MIT license, a shell with some functionalities in C# (.NET Core). You can contribute if you wish on
GitHub.
To solve the problem of matching a given token (part of the command line) with a builtin or a command, we use a dictionary.
However, we don't index the programs in the path for the moment. We just combine the name of the program with all the paths in the %PATH% variable.
Capture input
Expand environment variables, expand aliases
Try to match a builtin and run it if there is a match
Try to match with a program in %PATH% / $PATH
Run the program or display error
While you are unlikely to find the internal working of CMD (because it's closed source), you can find easily unix shell (bash, sh, zsh, etc..) information.
Links:
Bash Reference
Zsh Reference
TCSH Reference

Change application type with Mono.Cecil?

How can I modify an application from Console Application Type to Windows Application Type and vice versa with Mono.Cecil?
To convert a console .exe to windows .exe, you can use:
var file = "foo.exe";
var module = ModuleDefinition.ReadModule (file);
// module.Kind was previously ModuleKind.Console
module.Kind = ModuleKind.Windows;
module.Write (file);
The other way around is as simple as choosing the appropriate ModuleKind value. From Cecil's source:
public enum ModuleKind {
Dll,
Console,
Windows,
NetModule,
}
For people who needed more help on this like me :)
you may need the apt pacakge libmono-cecil-cil-dev
//mono-cecil-set-modulekind-windows.cs
using System;
using Mono.Cecil;
namespace CecilUtilsApp {
class CecilUtils {
static void Main(string[] args) {
var file = args[0];
var module = ModuleDefinition.ReadModule (file);
module.Kind = ModuleKind.Windows;
module.Write (file);
}
}
}
// -----
//Makefile
//mono-cecil-set-modulekind-eq-windows.exe:
// mcs $(shell pkg-config --libs mono-cecil) ./mono-cecil-set-modulekind-windows.cs
./mono-cecil-set-modulekind-windows.exe myprog.exe

Print the source filename and linenumber in C#

Is there any way to retrieve the current source filename and linenumber in C# code and print that value in the console output? Like LINE and FILE in C?
Please advise.
Many thanks
Anders Hejlsberg presented new API for that in BUILD keynote:
Print current file name, method name and line number
private static void Log(string text,
[CallerFilePath] string file = "",
[CallerMemberName] string member = "",
[CallerLineNumber] int line = 0)
{
Console.WriteLine("{0}_{1}({2}): {3}", Path.GetFileName(file), member, line, text);
}
Test:
Log(".NET rocks!");
Output:
Program.cs_Main(11): .NET rocks!
What's going on here?
You define a method with optional parameters and decorate them with special attributes. If you call method without passing actual arguments (leave defaults) - the Framework populates them for you.
This answer is outdated! See #taras' answer for more recent information.
No constant :(
What you can do is a lot uglier :
string currentFile = new System.Diagnostics.StackTrace(true).GetFrame(0).GetFileName();
int currentLine = new System.Diagnostics.StackTrace(true).GetFrame(0).GetFileLineNumber();
Works only when PDB files are available.
You can use the StackTrace object from the System.Diagnostics namespace but the information will only be available if the PDB files are there.
PDB files are generated by default for both the Debug and Release builds the only difference is that Debug is setup to generate a full debug info where as the Release build is setup to only generate a pdb (full/pdb-only).
Console.WriteLine(new StackTrace(true).GetFrame(0).GetFileName());
Console.WriteLine(new StackTrace(true).GetFrame(0).GetFileLineNumber());
There are no constants defined for that as of now.
The .NET way of doing it is using StackTrace class.
It however works only for Debug builds. So in case you use it, you can have the code using StackTrace between
#if DEBUG
//your StackTrace code here
#endif
You can read about using #if preprocessors for your DEBUG vs. RELEASE builds in the following Stackoverflow thread.
C# if/then directives for debug vs release
EDIT: Just in case you still need this debugging information in release builds, read the following answer on Stackoverflow:
Display lines number in Stack Trace for .NET assembly in Release mode
If you want some more internal detail, but you don't specifically need filename and line number, you can do something like this:
System.Diagnostics.Debug.Print(this.GetType().ToString() + " My Message");
This has an advantage over printing out the filename in that if you put this in a parent class, it will print out the child class name that is actually running the code.
If you wanted to write your own version of Debug.Assert, then here's a more complete answer:
// CC0, Public Domain
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System;
public static class Logger {
[Conditional("DEBUG")]
public static void Assert(bool condition, string msg,
[CallerFilePath] string file = "",
[CallerMemberName] string member = "",
[CallerLineNumber] int line = 0
)
{
// Debug.Assert opens a msg box and Trace only appears in
// a debugger, so implement our own.
if (!condition)
{
// Roughly follow style of C# error messages:
// > ideone.cs(14,11): error CS1585: Member modifier 'static' must precede the member type and name
Console.WriteLine($"{file}({line}): assert: in {member}: {msg}");
// Or more precisely match style with a fake error so error-parsing tools will detect it:
// Console.WriteLine($"{file}({line}): warning CS0: {msg}");
}
}
}
class Program {
static void Main(string[] args) {
Logger.Assert(1+1 == 4, "Why not!");
}
}
Try it online.

Categories