Asti's Notes
A DOS-like shell in .Net
And a simple tutorial for NetShell


Add a reference to NetShell, create a new DOS.cs file. The main file just calls NetShell’s RpcShell with an instance of DOS.

static int Main(string[] args)
    var shell = new RpcShell(new DOS()) { 
        Prompt = Environment.CurrentDirectory, FlagPrefix = "/" 
    return shell.Run();

With that out of the way, let’s implement the first command.


This is trivial. Add a Command attribute with the (optional) name and help-text.

public void Echo(string text) => Console.WriteLine(text);

Let’s see how that looks like:

Maybe add a bit of colour.

public void Echo(string text, ConsoleColor color = ConsoleColor.White)
	Console.ForegroundColor = color;	


cls is also trivial to implement.

public void Clear() => Console.Clear();


Of course we need an exit command - this just calls Shell.Exit

public void Exit(Shell shell) => shell.Exit(0);


This is as you’d expect, but you also need to change the current shell prompt. We’re using the built-in dependency injection to get the instance of the shell, and set the prompt.

public void ChangeDir([Suggest(nameof(SuggestDirs))] string directory, Shell shell)
    var path = Path.GetFullPath(directory);
    if (!Directory.Exists(path))
        throw new DirectoryNotFoundException($"{path} does not exist");

    shell.Prompt = Environment.CurrentDirectory = path;


Just return the file contents and it’ll be printed out automatically.

public string Type([Suggest(nameof(SuggestFiles))] string filename) => 
	File.ReadAllText(Path.Combine(Dir, filename));

And to auto-complete files in the current directory:

public IEnumerable<string> SuggestFiles() => 


Here we’re showing a simple dir command, but here’s how to add help-text to the command, and for each pattern.

[Command("dir", "Lists directories and files with an optional pattern")]
public IEnumerable<string> List(
	[Description("Accepts glob patterns")] string pattern = "*",
	[Description("Only show files if true")] bool files = false
	var dirs = files ? Enumerable.Empty<string>() : Directory.EnumerateDirectories(Dir, pattern);
	var allfiles = Directory.EnumerateFiles(Dir, pattern);
	return Enumerable.Concat(dirs, allfiles).Select(Path.GetFileName);

Now if we do a help dir we get:

D:\>help dir
Command dir Lists directories and files with an optional pattern
Syntax: dir (String [pattern] = *) (Boolean [files] = False)
/pattern    Accepts glob patterns
/files	    Only show files if true


Running applications: Anything that doesn’t match our commands, we can attempt to run with the given command-line - this is DOS' famous “Bad command or file name”. We can do that with the DefaultCommand attribute which marks a catch-all method.

The following implementation redirects the stdin/stdout of our console to the program, till it’s terminated.

public void Execute(string name, string[] args)
    using (var process = Process.Start(new ProcessStartInfo(name, String.Join(" ", args)) { RedirectStandardOutput = true, UseShellExecute = false }))
        while (!process.StandardOutput.EndOfStream)
            if (Console.KeyAvailable)

            if (process.StandardOutput.Peek() != -1)

Our demo isn’t complete until we run the Windows Command Prompt inside our command prompt!

Last modified on 2020-02-18

Comments Disabled.