So You Want To Make A Command Line Interface




We have no real idea how [Keba] intends to implement a system for the ATmega16 (Serial display? Output to an LCD? etc?), but for my project it is as follows. Using C# along with DirectX (can you tell I’m making a game with a developer console?) I’ll display an input line, suggestions for inputs (intellisense), and outputs based only when a correct input is given.

To begin, and to stay focused on only the CLI, I’ll assume your project has all the necessary startup and load functions. In my case, loading of a DX device, and input handling. Also, we assume you know how to program in your respective language.

I’ll be using a pretty advanced technique (StringBuilder) for string handling, because traditional string + string concatenation is terrible on memory (and games need as much as they can get). If you don’t care for memory, you can simply use regular strings.

To start off we’ll need some global variables,

public bool bool_isConsoleOpen = false; //console, also known as CLI
public StringBuilder StringBuilder_Console = new StringBuilder(); //could be replaced with string
public InputDevice ID = new InputDevice();

Within the main function loop, make a call to a method named UpdateConsole();

Now, in my setup to prevent unwanted user input there is a small check to see if the console is ‘open’ or ‘closed’.

public void UpdateConsole()
 //opening console
 if (ID.isKeyDown(Keys.Oemtilde) &amp;&amp; ID.isOldKeyUp(Keys.Oemtilde))
  if (bool_isConsoleOpen == false)
   bool_isConsoleOpen = true; //user pressed magic key, open console
   StringBuilder_Console = new StringBuilder(); //clear string
   bool_isConsoleOpen = false; //user pressed magic key, close console

The next section of code handles all the inputs (keyboard presses) and builds our string that is about to be entered. It includes support for shift capitals, pasting from the clipboard, and also checks to make sure each key entered is allowed. Simply add this portion immediately after bool_isConsoleOpen = false;.

//appending console if its open.
if (bool_isConsoleOpen == true)
 bool caps = false; //variable that helps determine if shift is pressed
 if (ID.isKeyDown(Keys.ShiftKey))
  caps = true;

 List pressedkeystemp = ID.PressedKeys; //I had to modify my ID a bit to make it get a list/array of the keys pressed.
 //go through each new key in list
 foreach (Keys currentkey in pressedkeystemp)
   //make a string, this is for numbers
   string key;

   //if the key SPACE is pressed, make a space
   if (currentkey == Keys.Space)
    StringBuilder_Console.Append(&quot; &quot;);
   //if the key BACK is pressed, backspace
   else if (currentkey == Keys.Back)
    if (StringBuilder_Console.Length &gt; 0)
     StringBuilder_Console.Remove(StringBuilder_Console.Length - 1, 1);
   //if enter is pressed
   else if (currentkey == Keys.Enter)
    //send it off to apply our data
    //clear our string
    StringBuilder_Console = new StringBuilder();
   //if a number is pressed, make it show up
   else if (StringKeyINTCheck(currentkey, out key))
   //if a-z is pressed, make it show up
   else if (StringKeyCheck(currentkey))
    // if V was just pressed and either control key is down
    if (currentkey == Keys.V &amp;&amp; (ID.isKeyDown(Keys.ControlKey)))
     // paste time!
     string pastevalue = &quot;&quot;;
     pastevalue = System.Windows.Forms.Clipboard.GetText(System.Windows.Forms.TextDataFormat.Text);
    // if not pasting, do a regular key
    else if (!caps)
    else if (caps)

In order to prevent some characters from being printed, such as alt characters, and to make sure the input key can actually be displayed (otherwise you could crash with error) I implement a few checks. You’ll notice I have two different types, Check(input, output) and Check(input). The former is necessary because often the input is the ASCII value, and needs to be converted to a char or string before being added to the builder. The latter simply returns true or false if the key is valid.

Example of the first, numerals

private bool StringKeyINTCheck(Keys key, out string i)
 if (key == Keys.D1 || key == Keys.NumPad1)
  i = &quot;1&quot;;
  return true;
 else if (key == Keys.D2 || key == Keys.NumPad2)
  i = &quot;2&quot;;
  return true;

And the latter, a-z

private bool StringKeyCheck(Keys key)
 if (key == Keys.A ||
                key == Keys.B ||
                key == Keys.C ||
                key == Keys.X ||
                key == Keys.Y ||
                key == Keys.Z)
  return true;
  return false;

So now we have our string built, you’ll notice the new method ApplicationSettings(string) is called whenever enter is pressed. This is the sending off of the string the user just typed in/that we built, we must now break that string down and determine what the user typed, and what should happen.

Once again, I start off with a few checks, just to prevent crashes.

private void ApplicationSettings(string temp)
 if (temp != null) //make sure the user didn't type in &quot;&quot;.
  //make it all lower case
  temp = temp.ToLower();

  //split by spaces
  string[] words = temp.Split(' ');

Now comes the fun part, We’ve assumed the user has entered things such as “quit” “fullscreen 1” and “pos 100x100x100”. The first will quit the application, the second will determine if the application should be fullscreen or not. And the final sets the users XYZ position in space. These three are simply examples of multiple variable entry, and you could of course program whatever you need.

Immediately after string[] words = temp.Split(‘ ‘); add the following,

 //quit exit
 if (words[0] == &quot;quit&quot; || words[0] == &quot;exit&quot;)

 //check for users fullscreen preference
 else if (words[0] == &quot;fullscreen&quot;)
  if (words[1] == &quot;0&quot;)
   WindowedMode = true; //arbitrary global named windowedMode
  else if (words[1] == &quot;1&quot;)
   WindowedMode = false;

 //set the camera position
 else if (words[0] == &quot;pos&quot;)
  if (words[1].Contains(&quot;x&quot;))
   string[] res = words[1].Split('x');
   int int_x = Convert.ToInt32(res[0]);
   int int_y = Convert.ToInt32(res[1]);
   int int_z = Convert.ToInt32(res[2]);

   Cam.Position = new Vector3(int_x, int_y, int_z);//arbitrary class camera Cam

catch (IndexOutOfRangeException e)
 //this occurs when the user types &quot;fullscreen $&quot;. Where $ is a variable, and the user typed nothing.
 //do nothing we should tell the user this with an error message.
catch (FormatException e)
 //this occurs when the user types &quot;resolution $x$&quot;, where $ is an int variable, and the user typed alpha.

You probably could stop here if needed, you have input and output. However, I have something like 40 different commands in the current revision of my console, I couldn’t remember them all. So I made my own nifty intellisense.

This is going to require setting up another global–string list, filling it with commands, and then alphabetizing it.

List&lt;string&gt; ListString_Console = new List&lt;string&gt;();

private void LoadConsoleWordList()

 //load in our console!

 //sort our list

Now at the bottom of our UpdateConsole().

if (bool_isConsoleOpen == true)
 BMF_Arial.AddString(StringBuilder_Console.ToString() + &quot;_&quot;, &quot;console&quot;, new System.Drawing.RectangleF(5, 18, Resolution.Width, 20)); //how I draw things to the screen in DX. StringBuilder_Console is the string we built earlier, so the user can see what he is typing.

 //help our user search.
 int q = 35;

 //check every single string we know against what the user is typing in
 foreach (string stringy in ListString_Console)
  //so long as the length is right, we continue
  if (stringy.Length &gt;= StringBuilder_Console.Length) //this part could be eliminated, and we could simply go through every letter. But this speeds up operations a smidge.
   //temporary bool
   bool hodling = false;

   //go through every letter
   for (int i = 0; i &lt; StringBuilder_Console.Length; i++)
    if (stringy[i] == StringBuilder_Console[i])
     hodling = true;
     hodling = false;

   //if it's a 100% match
   if (hodling)
    //draw it, and update q relative.
    BMF_Arial.AddString(stringy, &quot;console&quot;, new RectangleF(5, 2 + q, Resolution.Width, 20)); //these are all the matches to the currently types string.
    q += 18;

So how does it finally look?

No console open,

Hitting the magical key opens up console, begin typing, see intellisense,

Continue typing, other words that don’t match get taken off display,

and hitting enter executes the command,

42 thoughts on “So You Want To Make A Command Line Interface

  1. “private bool StringKeyCheck(Keys key)
    if (key == Keys.A ||
    key == Keys.B ||
    key == Keys.C ||
    key == Keys.X ||
    key == Keys.Y ||
    key == Keys.Z)

    you are for real?

    Ok, first off, because you checking for equality in an or, each one of those need to be checked until one returns true. This means you could end up with 26 checks for equality.

    I haven’t compiled this, but I will when I get home. However, here is what you should be doing.

    private bool StringKeyCheck(Keys key)
    return (key > 64 && key = (int)Keys.A && (int)key <= (int)key.Z);

    Now, I don't have a compiler with me, so I don't know if those explicit casts are needed in the second one.

  2. @Dick: || is short-circuiting (“lazy”) so it stops evaluating conditions when it’s encountered the first one that is true.

    However, I would note that you should use ToLowerInvariant() or ToUpperInvariant() rather than ToLower() or ToUpper() when comparing keywords. ToLower() and ToUpper() operate according to the current culture setting, so on a Turkish computer “QUIT”.ToLower() returns “quıt”. ToLowerInvariant() uses the invariant culture, which is basically English. Look up the “Turkey test” for more information.

    It is generally “correct” to perform operations according to the user’s locale, but in certain situations – especially when parsing configuration files or the like – it’s more practical to use a standard, and the invariant culture is a simple one that most should be familiar with.

  3. That StringKeyCheck made my eyes sad.

    @Ben Ryves: I think he knew that, as he was saying you “could” end up with 26 checks for equality (when key == Keys.Z).

    Anywho, it is really considered bad programming style to use that many condition checks. You could at least have made a loop…

  4. @Ben Ryves
    I understand that
    From my comment:

    Note “checked until one returns true” and “could end up”

    private static bool StringKeyCheck( Keys key )
    return (key >= Keys.A && key <= Keys.Z) ? true : false;

    This is tested…

  5. I agree with dick, only cause of my horrible programming on slow systems

    it works great if you happen to hit A a lot, but when your waiting for (sometimes) seconds to return Z you start looking for different ways (not necessarily better as in my case)

  6. well. if you are able to emulate some basic features of VT100 compatible terminal (you need to handle some escape sequentions especialy newline (\n) and carriage return (\r) – this enables you to modify the last line by moving cursor to i’s beginning. and maybe few more escapes if needed – colors, hiding password input, ncurses support) you can use code from any linux shell (busybox compiled only with ash applet can be ported easily i guess…). (any microcontroler with (GC)C cross-compiler available).

    you can also use readline (command history) and m4 (some macros or something simillar) libraries for most of those things (they are probably used in shells, so you don’t need to use whole shell).

    if you need whole terminal on (only the part for rendering proper characters on proper place of screen) you can embed xterm (this can be obviously done only where xterm is available) to your software (probably no go for microcontrolers).

  7. generally it’s better accepted to use

    if (someBool) {…}
    – or –
    if (!someBool) {…}

    rather than:

    (if someBool == true)
    -or –
    (if someBool == false)

    also the following:

    bool caps = false;
    if (ID.isKeyDown(Keys.ShiftKey))
    caps = true;

    could be better represented with:

    bool caps = ID.isKeyDown(Keys.ShiftKey))

    Not sure of the iterations that we’re talking about, but if this were something that was going to be looped through a bunch of times (like in a game or something like that) the more optimized you can make your code, the faster it will be.

    Not trying to pick on your code, I’m sure everyone would have a hayday with mine. Cool post, though… thanks!


  8. @corster
    You suggested:
    if (!someBool) {…}

    rather than:

    (if someBool == false)

    while you are right, this is better style, it would compile into the same thing. So, generally something you would overlook during a code review.

  9. I’m actually really glad you guys are finding holes in my code, I love it when I can learn from an experience and it makes me a better programmer. Please keep the comments coming. Infact, I can’t believe I didn’t think to use JeffR’s idea in the first place, and future revisions I certainly will.

    I will however, like to point out Dick, HAD is leaning towards easier and simpler hacks (it’ll be way more apparent in future how-tos), I wanted to give the user the ability to check each and every key, so they know what and why certain keys are showing up.

    Its not in this article, but in my actual implementation I have it check special characters (. / etc and only allows certain ones. These checks require individual ifs, no matter what.

    And as you said yourself, compilers are smart. Enough such that now adays they can understand extended if statements. A quick timekeeping of 5,000,000 iterations will result in the same amount of time (near plank time for exaggerated effect), regardless of your or my techniques.

    Plus, one of ours uses excessive memory with casts *whistles*. Granted, you’re correct there are 26 (even if they take virtually no time) checks potentially. Its really the programmers choice, memory or cpu.

    I just can’t wait for the hardcore nerds to point out “inefficient string == string” comparisons.

    Have a nice day!
    Jakob Griffith
    HackaDay Team

  10. I wrote a command line for the Macintosh (nShell) in The Olde Days. You know, back when Apple said “why would anyone need a command line for the Mac?”

    Anyway, I found the O’Reilly book “UNIX Power Tools” to be invaluable. It scans many shells and their various strengths.

    Have fun. Adding features is a bit addictive.

  11. Hey Jakob,

    I’m glad to see commenting in on this. You are right in that as far as the execution time goes. However I would doubt that the compiler will reduce an “extended” if, composed of “or” to a simple range check. That being said you could easily check ranges, and individual keys within the same if.


    if( (key = Keys.Z) || (key == Keys.Ctrl || key.Shift ) )


    //do stuff


    this would still cut down execution time, and greatly reduces the over head of a poorly written if statement.

    I wasn’t going to point it out, but seeming as you asked for further suggestion you have a design flaw in the 6th code segment. I’m not sure if you are aware (I really don’t know how long you have been programming), but try/catches are actually really slow. I’m not saying they shouldn’t be there, because they should, but when an exception is thrown there is a real performance hit. Essentially it boils down to the fact that your application has to “walk the stack” looking for the first catch statement. That being said, for your string to int cast, I would suggest you use the Int32.TryParse method. Also, to avoid an index out of range exception simply check the length of the array. For instance, before doing anything make sure there is something in the array. words.Length = 1. And, when you check for “fullscreen”, if( words.length > 1 && words[0] == “fullscreen” ). Simple as that, and you dont end up thrown an exception for no real reason.

    Article on exceptions:

  12. ok.. im just going to say it…

    if (key is greater than or equal to Keys.A
    AND keys is less than or equal to Keys.Z)
    OR (key equals Keys.Ctrl OR key equals Keys.Shift )

  13. @Jakob Griffith

    Well, you asked for it:
    It is in fact better to use string.equals().
    The usage can make up to 8% difference in execution time (see for reference).

    And as you stated your application is time-critical, you should use any optimization you can get.

    btw: I don’t feel like a hardcore nerd though. ;)


  14. Well, I’ve got to say Jakob, aside from that first “twitter client” blunder, you’ve made a great addition to HAD. Your posts are now often the least disappointed-facepalm-ish posts I read here.

    The only problem I have with creating CLIs is trying to find a nice compact way to store the potential command set. lately it’s just been a set of strings stored in a binary file. I also tend to convert all entered text to uppercase and store command keywords as all-uppercase to eliminate case-matching problems.

  15. Hi
    I appreciate your extensive explanation, it has given me quite a few ideas for my project. I will definitely adapt some of your methods. Here is some background.

    After doing projects in C with my ATmega16, involving simple LED’s, clocks with delays and timers, ADC, a 4-line LCD and an IR remote control, I decided it was time to try out USART.

    I assembled the hardware on my prototyping board, and after tracking down some communication errors, I managed to establish a connection from my PC to the AVR. I made it switch a LED on and off, by writing commands in the console. This was done by comparing strings.

    On a similar note, last semester, my group and I built a small computer based on the Motorola 68k processor, programmed to regulate the temperature in a vacation-house, by input from a GSM text-message. The user could send messages like “R 1 T 35,5”, and the system would then turn on temperature-Regulation, and try to keep the Temperature at 35,5 Degrees Celsius, by use of a heat-lamp. This was done without any built in functions, meaning I split up each command, and compared that to strings, character for character. The temperature was deciphered by comparing “35,5” to every string, in a long string-array of temperatures, and then saving the array-number. The system then replied in a text-message to the user, just like a console.
    As you may guess, that required a ton of for-loops, but it ended up working great, although the code was quite ugly.

    This made me want a better interface. As I figured others might have similar wishes with their projects, I asked here, so others could benefit too.

  16. @Dick:
    > if (!someBool) […] (if someBool == false) […]
    > would compile into the same thing.

    For “false” it should, but for “true” and non-bools (avr-gcc, for instance, has no native bool type!) I’d be surprised if it would.

    if (someInt) …equals… if (someInt > 0)
    if (someInt == true) …equals… if (someInt == 1)

    …which are obviously totally different things.

  17. Is it possible to have very long articles like this formatted for the RSS feed so that “after the jump” does what it says, and presents a shortened version? Otherwise it makes for a suddenly very cluttered feed…

    Good article though, thanks – bookmarked for future use!

  18. Hi Jakob,

    Notwithstanding the optimisations that Dick and BeatJunkie suggest, have you considered using a switch statement for your command identification? I know that it’s not as syntactically neat in C# as it would be in C or C++, but surely it’s still neater than a string of if {…} else if {…} else if {…} statements?

    Also, where you’re iterating through the characters looking for matching strings, I’d use the string.StartsWith() function rather than doing the character iteration.

    Hopefully you’re taking all of this as constructive criticism :)

  19. @Matthias_H

    i have no experience with avr-gcc, however we are talking about C# here.
    if( !someBool ) and if( someBool == false ) will, as I said, compile into the same thing.
    Same can be said for “true”, because C# does have a native boolean type.

    For your latter example with regards to ints, I would be inclined to agree. However confirming that would be rather easy, I believe gcc compilers have a decompiler built in. Actually, Visual Studio has one as well. You bring up a good point, just outside of the .Net world.

  20. Here’s a rundown of what jumped out at me while reading this – I’m sure there’s more, but this is just the obvious language problems. Some of this has already been stated. Please take this constructively – you’re on the right track, but your code style needs a lot of work.

    First, there are bad and inconsistent naming conventions throughout. StringBuilder_Console, StringKeyINTCheck, pressedkeystemp, ListString_Console, stringy, hodling, etc. Variables should be named and cased consistently, with clear names that describe what they are or do, using camelCase or CamelCase as appropriate, and avoiding Hungarian notation (int_x) whenever possible.

    There are way too many comments throughout the code. Things like “clear string”, “if enter is pressed”, “paste time!”, “make it all lower case”, next to code that obviously does what you’re saying it does, only serves to distract, and serves no useful purpose whatsoever. Properly written code is self-documenting, and comments should be limited to useful information, such as “why” things get done, instead of “what” is being done, especially on trivial tasks.

    “[…] a pretty advanced technique (StringBuilder) for string handling […]”
    – StringBuilder is not an advanced technique – it’s been around and talked about and used heavily for as long as .NET has been around. But even with that said, on something this small, there’s no way it would make any noticeable difference in performance or memory.

    “if (bool_isConsoleOpen == false)”
    – It’s much more accepted to do “if (!someBoolean)” instead of “if (someBoolen == false)”, even if they actually mean exactly the same thing.

    “List pressedkeystemp = ID.PressedKeys; // […]
    foreach (Keys currentkey in pressedkeystemp)”
    – Do “foreach (Keys currentkey in ID.PressedKeys)” instead. Much better to not use temp variables like this unless it provides some readability benefit or serves an actual purpose.
    – Not sure what this “List” class is, but it seems like a bad idea, whatever it is. Either a specific KeysCollection or a generic System.Collections.Generic.List would be better if possible, if this is actually your code, not part of DirectX.

    “string pastevalue = “”;
    pastevalue = System.Windows.Forms.Clipboard.[…]”
    – That initial value of “” is never used and is unnecessary. Should be “string pastevalue = System.Windows.Format.Clipboard.[…]”
    – It’s generally more accepted to use string.Empty instead of “”. Compiles to the same thing, but I think you’ll find the majority of people with opinions one way or the other prefer string.Empty.

    “if (key == Keys.A || key == Keys.B […])
    return true;
    return false;”
    – Aside from the fact that you can probably do this with a comparison (something like key >= Keys.A && key ((s.Length >= consoleValue.Length) && s.StartsWith(consoleValue))); foreach (string match in matches) { BMF_Arial[…] }

    “tagged: […] c++ […]”
    – This is C#, not C++. Quite a bit different.

  21. AUGH

    If you’re thinking about creating a new command line interface… STOP.

    There are dozens of embeddable CLI frameworks from simple Forth-like single-command tools to full programming languages like Tcl. Add your commands as callbacks to the framework, and get back to work on something worth doing.

  22. Hey wtf? it already allow for it? quick test using a copy from another comment:

    private bool StringKeyCheck(Keys key)

  23. I just wanted to leave a quick comment here. While I may not address you individually, I am reading each and every reply. And I’ll be incorporating new techniques for future programing tutorials (we have a big one planned, its going to be great!). Thank you.

    However, Joe Enos, I really don’t agree with some of your practices. Formost, there is no such thing as too many comments. But, after some consideration, I do understand your point about “why something works” instead of “how something works”.

    Jakob Griffith
    HackaDay Team

  24. I would have to agree with Joe Enos, especially the naming which is all mixed up.

    I think Joe was merely trying to help improve your coding as these are fairly common considerations with C#, and the coding conventions generally apply over many different languages.

    In regards to comments, an excess does not always improve the quality, ideally the code should be self-documenting to reduce the quantity of comments. Take for instance:

    I think it was a great post and helpful though would be improved by taking what Joe has said into account.

  25. vt100 emulation is much harder, if you do it right. Back in the ’80s I spent over a year, off and on, catching special cases to successfully emulate a vt00 well enough that I could use VMS and RSX applications (EDT, RMD, etc) through it. I think the code on is pretty close to the final version (that’s been lost) – it takes vt100 codes on input and generates escape sequences for your current terminal using the UNIX termcap library.

  26. The last section bothers me UpdateConsole(), as it is allocating it a boolean within a for loop, inside of an if, and in terms of memory and performance it would be better to allocate the bool before the foreach, and then set it back to false before the end of the for loop. Again, a minor optimization but it’s there.

    As others pointed out there are some parts that more experienced coders flinch from, but it was a good tutorial so thanks, and from the beginner programmer perspective I’d actual enjoy the ability to optimize some of this code later makes it more enjoyable, and is something I can look for. And he is right that for explaining things to people some faster notations are more confusing. (And who doesn’t like it when they have an easy spot to test them out and prove the optimization works in code that already works otherwise).

  27. @pfffffff: Doesn’t C# have an equivalent of isprint()?

    @WickedShell: surely the C# code generator doesn’t actually allocate and release storage when it can hoist it outside the loop? That’s a pretty basic optimization.

    And I still think the right answer to “So you want to make a command line interface” is “no, you don’t”. This is SUCH a solved problem, with many excellent CLI parsing frameworks that come along with hooks allowing things like cross-application scripting…

  28. @Peter da Silva
    “So you want to make a command line interface”
    “no, you don’t”

    The correct answer to your post is just to ignore like everyone else has.
    I want to help educate you though.
    What you are talking about is not reinventing the wheel but what you do not understand likely due to inexperience in programming is that not every wheel previously made is a right fit. Especially for gaming applications but then again using c# for a game is a mystery to me. There are also licensing concerns, finding the right or closest fit, and modifying/adapting another persons code.
    By the time you work through all of that most people will have already created their own.

    For the rest, most of you are comparable to school children with your responses trying to find every little thing to critique to make yourselves feel superior in programming. Put up some code and we will all critique it.

    This goes doubly for anyone who would be silly enough to list ! vs = false / = true comments.
    Just wow, you guys need a true hobby, if you were professionals you would be fired for wasting time on something that stupid.

  29. Neuntoter: I have written dozens of command line interfaces over the years, starting back in the ’70s when there weren’t any freely redistributable libraries available. There are dozens of great CLIs available now… the one I use most often is Tcl, mainly because I was heavily involved in its design and development, but there are many more to choose from. If you prefer a more Pascal-like style, there’s Lua (which is widely used in gaming already, and reportedly an excellent fit). If you prefer a more UNIX-shell-like style, there’s Perl. There’s Python, and Ruby, and for the true old-school types there’s a ton of Lisp and Forth based scripting front ends.

    Licensing concerns are real, so you should choose a CLI distributed under the BSD or MIT licenses. There are still dozens of examples, many of which are exceptionally easy to interface to C-like languages.

    The only reason to reinvent this wheel is for personal amusement and education. For production code you’re far better off picking your wheels off the open source vine.

Leave a Reply to ugh Cancel reply
