I am aware that this is nothing new and has been done several times. But I am looking for some reference implementation (or even just reference design) as a "best practices guide". We have a real-time embedded environment and the idea is to be able to use a "debug shell" in order to invoke some commands. Example: "SomeDevice print reg xyz" will request the SomeDevice sub-system to print the value of the register named xyz.
-
What functionality are you looking for? Parsing a few words and calling functions isn't hard at all, but if you want quoting, etc, it gets more difficult. – Michael Kohne Dec 16 '09 at 13:42
-
I do not know what is "quoting". I am looking mainly for the "parsing keywords and calling functions" part. However, it needs to be generic and scalable. We also want to have history, help, tab-completion etc. But at its core, it should still be about parsing words and calling functions. There will probably be a main module that parses the first one or two keywords and then passes control to another module for the rest of the parsing and command completion etc. Nothing complicated. But is there an existing implementation I can look at? – venk Dec 17 '09 at 09:40
-
In the implementation I suggested, history and help are simply implemented as functions (since you can call any function with global scope). The shell expression evaluator maintains a history buffer and the h() function (parentheses were optional for simple calls, so just 'h' on the command line), provided a numbered list of the buffer and prompted for selection. vxWorks also uses vi-like cursor control to allow scrolling through the history buffer; I chose not to do that. – Clifford Dec 17 '09 at 10:35
-
"quoting etc."!? What is that? – Clifford Dec 17 '09 at 10:36
-
I assume "quoting" is reference to being able to quote an command line parameter such as a filename with spaces, escape sequences (e.g. \n, \t, \r), and similar "intelligent" interpretation while parsing the command line. – mctylr Aug 19 '10 at 14:33
7 Answers
I have a small set of routines that is essentially made up of 3 functions and a lookup table:
- a function that gathers a command line - it's simple; there's no command line history or anything, just the ability to backspace or press escape to discard the whole thing. But if I thought fancier editing capabilities were needed, it wouldn't be too hard to add them here.
- a function that parses a line of text argc/argv style (see Parse string into argv/argc for some ideas on this)
a function that takes the first arg on the parsed command line and looks it up in a table of commands & function pointers to determine which function to call for the command, so the command handlers just need to match the prototype:
int command_handler( int argc, char* argv[]);
Then that function is called with the appropriate argc/argv parameters.
Actually, the lookup table also has pointers to basic help text for each command, and if the command is followed by '-?' or '/?' that bit of help text is displayed. Also, if 'help' is used for a command, the command table is dumped (possible only a subset if a parameter is passed to the 'help' command).
Sorry, I can't post the actual source - but it's pretty simple and straight forward to implement, and functional enough for pretty much all the command line handling needs I've had for embedded systems development.

- 1
- 1

- 333,147
- 50
- 533
- 760
-
While none of the answers here is a perfect match, I realized that Michael's response comes closest. I agree that an early version of the CLI anyone wrote might have looked something like this. However, over a period of time, one would expect that some improvements must have crept in due to the following considerations (a)scalability (b)genericness (c)Good programming and/or design practices etc. Basically, I am looking for something that has improved with age/experience etc and not an extremely basic version. Any inputs/pointers could be very helpful. – venk Jan 31 '10 at 12:59
-
1As an example, a very straightforward improvement over the above design (for a large system) would be to have a convention that the first arg should always be the name of a sub-system within a potentially very large system. The first level logic would then focus only on the first arg and pass on the entire command to the intended sub-system. Each subsystem would then be responsible for looking up its own function table pointer etc. – venk Jan 31 '10 at 13:08
You might bristle at this response, but many years ago we did something like this for a large-scale embedded telecom system using lex/yacc (nowadays I guess it would be flex/bison, this was literally 20 years ago).
Define your grammar, define ranges for parameters, etc... and then let lex/yacc generate the code.
There is a bit of a learning curve, as opposed to rolling a 1-off custom implementation, but then you can extend the grammar, add new commands & parameters, change ranges, etc... extremely quickly.

- 10,303
- 5
- 36
- 53
-
If you have never used Lex/Yacc (or GNU's Flex/Bison), then the lex & yacc book by John Levine et all, is a nice introduction. http://oreilly.com/catalog/9781565920002 I have not had a chance to look at it's latest reincarnation, Flex & Bison by John Levine http://oreilly.com/catalog/9780596155988/ so that edition may be worth looking for instead. – mctylr Aug 19 '10 at 14:28
You could check out libcli. It emulates Cisco's CLI and apparently also includes a telnet server. That might be more than you are looking for, but it might still be useful as a reference.

- 211,373
- 23
- 97
- 98
-
I was going to recommend the same thing. How directly applicable it is depends on how sophisticated your embedded platform is. – Dipstick Dec 16 '09 at 13:13
-
Thanks. libcli is good but uses sockets etc and is probably an overkill for us. – venk Jan 31 '10 at 13:08
If your needs are quite basic, a debug menu which accepts simple keystrokes, rather than a command shell, is one way of doing this.
For registers and RAM, you could have a sub-menu which just does a memory dump on demand.
Likewise, to enable or disable individual features, you can control them via keystrokes from the main menu or sub-menus.
One way of implementing this is via a simple state machine. Each screen has a corresponding state which waits for a keystroke, and then changes state and/or updates the screen as required.

- 2,620
- 1
- 22
- 24
-
The problem with this is that if you later find that it's too restrictive (menus never have the expressiveness of a full-blown command line), then it can be a big job to go back and re-write everything to use a command line instead. If you think you might need the flexibility of a command line, then it's better to do it right the first time. – Dan Moulding Dec 16 '09 at 14:02
-
@Dan: agreed. It very much depends on what you think your requirements will be. – Steve Melnikoff Dec 16 '09 at 17:35
vxWorks includes a command shell, that embeds the symbol table and implements a C expression evaluator so that you can call functions, evaluate expressions, and access global symbols at runtime. The expression evaluator supports integer and string constants.
When I worked on a project that migrated from vxWorks to embOS, I implemented the same functionality. Embedding the symbol table required a bit of gymnastics since it does not exist until after linking. I used a post-build step to parse the output of the GNU nm tool for create a symbol table as a separate load module. In an earlier version I did not embed the symbol table at all, but rather created a host-shell program that ran on the development host where the symbol table resided, and communicated with a debug stub on the target that could perform function calls to arbitrary addresses and read/write arbitrary memory. This approach is better suited to memory constrained devices, but you have to be careful that the symbol table you are using and the code on the target are for the same build. Again that was an idea I borrowed from vxWorks, which supports both teh target and host based shell with the same functionality. For the host shell vxWorks checksums the code to ensure the symbol table matches; in my case it was a manual (and error prone) process, which is why I implemented the embedded symbol table.
Although initially I only implemented memory read/write and function call capability I later added an expression evaluator based on the algorithm (but not the code) described here. Then after that I added simple scripting capabilities in the form of if-else, while, and procedure call constructs (using a very simple non-C syntax). So if you wanted new functionality or test, you could either write a new function, or create a script (if performance was not an issue), so the functions were rather like 'built-ins' to the scripting language.
To perform the arbitrary function calls, I used a function pointer typedef that took an arbitrarily large (24) number of arguments, then using the symbol table, you find the function address, cast it to the function pointer type, and pass it the real arguments, plus enough dummy arguments to make up the expected number and thus create a suitable (if wasteful) maintain stack frame.
On other systems I have implemented a Forth threaded interpreter, which is a very simple language to implement, but has a less than user friendly syntax perhaps. You could equally embed an existing solution such as Lua or Ch.

- 88,407
- 13
- 85
- 165
One alternative is to use a very simple binary protocol to transfer the data you need, and then make a user interface on the PC, using e.g. Python or whatever is your favourite development tool.
The advantage is that it minimises the code in the embedded device, and shifts as much of it as possible to the PC side. That's good because:
- It uses up less embedded code space—much of the code is on the PC instead.
- In many cases it's easier to develop a given functionality on the PC, with the PC's greater tools and resources.
- It gives you more interface options. You can use just a command line interface if you want. Or, you could go for a GUI, with graphs, data logging, whatever fancy stuff you might want.
- It gives you flexibility. Embedded code is harder to upgrade than PC code. You can change and improve your PC-based tool whenever you want, without having to make any changes to the embedded device.
If you want to look at variables—If your PC tool is able to read the ELF file generated by the linker, then it can find out a variable's location from the symbol table. Even better, read the DWARF debug data and know the variable's type as well. Then all you need is a "read-memory" protocol message on the embedded device to get the data, and the PC does the decoding and displaying.

- 41,871
- 30
- 130
- 181
For a small lightweight thing you could use forth. Its easy to get going ( forth kernels are SMALL) look at figForth, LINa and GnuForth.
Disclaimer: I don't Forth, but openboot and the PCI bus do, and I;ve used them and they work really well.
Alternative UI's
Deploy a web sever on your embedded device instead. Even serial will work with SLIP and the UI can be reasonably complex ( or even serve up a JAR and get really really complex.
If you really need a CLI, then you can point at a link and get a telnet.

- 3,705
- 24
- 37