2

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?

1
  • First of all I think it's too broad. To pick "command name" a simple regex may be enough but then what to do with its arguments? Do you want to raw forward them to each command (as shell - more or less - does?). Over that you'll need to build a layer for parameter parsing as you do in your own console applications. Like this it's little work (you may use reflection to pick a command/class to instantiate from its name). That said...don't you think a proper parser is better? Just to mention something you can directly invoke from C# I'd say...**PowerShell**. Commented Oct 14, 2015 at 21:41

2 Answers 2

3

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
$ ...
Sign up to request clarification or add additional context in comments.

4 Comments

Nice, but your implementation would not handle quoted paths very well.
Very right. You'd need a different parsing logic for that (for-loop, Regex etc.). I leave this "as an exercise for the reader" (and OP) :-). You could also extend this to search for programs in a PATH variable for any commands which are not found.
@MaximilianGerhardt the solution is nicer then what I did, but I still think it could be done better... more dynamically.... I think what should be done, is first argument specifies the name of the class and second the function and so on... but I do not know how to do that... I mean with Dictionaries it is not practical, cmd or terminal have thousands of functions, that would not be practical for someone to hard code them all...
You can dynamically find classes and execute functions in them using "reflection", lookup that topic if you want to try an implementation :). And no, terminals only have a limited amount of built-in commands (check 'help' in your cmd.exe or linux-shell, maybe up to 30), so storing the name and a corresponding function in it is still feasable. The rest is just executing existing programs on your harddrive. You can even move the implementation to different files using the "partial" identifier in your class.
2

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.

  1. Capture input
  2. Expand environment variables, expand aliases
  3. Try to match a builtin and run it if there is a match
  4. Try to match with a program in %PATH% / $PATH
  5. 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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.