There are some things every decent developer has to have done (well, at least with some C++ background):
- write his own collection/tree classes
- write his own string class
- write his own console class
With the advent of standard classes (e.g. stl, MFC, or others in the C++ world, the java library and .NET base class library in the post-C++ world) writing ones own hasttable or string class does not really make sense anymore (perhaps appart from academic interest or educational purposes). With console applications the situation is somewhat different because as of yet there is no standard or widespread library. Yet the demands for any non-trivial console application are reoccuring, boilerplate and tedious to do again and again and again.
What are console applications usefull for in the first place?
- Console applications are a necessity if to be used in batch files. And batch files are a necessity if one wants to automate tasks. Automate tasks on the other hand is the dayly business of administrators and with continuous intergration it has become the daily business of developers as well.
- Console applications are the better services. If you thought of writing a service or some other kind of background application just to trigger some action at certain times or time intervals think again. The windows scheduler (or cron or whatever) already handles scheduling quite flexible. A console application registered with such as system will do the trick at cheaper cost, less effort, and provide more freedom in usage.
- For experienced users the console usually offers more flexibility and is more efficient to use than GUI interfaces.
Additionally, virtally any “enterprise application” actually is a familiy of applications, not only consisting of the main applications. Usually there are installation tasks, cleanup jobs, data import and export, nightly processing, diagnostics and maintenance tasks, and whatever else to do. Some of these tasks may be better done within a special environment (such as SQL jobs) but on the other hand, how do you have a SQL procedure participate in your applications error tracing?
There may be a time when build tasks (MSBuild, (N)ant, etc.) or libraries tailored for Monad (newly christened PowerShell) will become a standard. But this time is not due tomorrow and probably not next year. Until then I have to admit I’m a fan of console applications. (Call it DOS nostalgia if you like. ;-)).
Speaking of PowerShell, the new command line shell Microsoft proposes, it may be just arround the corner. Betas are available (even a first RC) and are very promising. There is also some work done to maximize reuse of functionality between PowerShell, the new management console (MMC 3.0), and WMI. And all Microsoft server products will entually provide PowerShell support. As Bob Muglia (Microsofts Senior Vice President, Windows Server) said on the last PDC: “We are going to undergo a project over the next few years to get a full set of Monad commands across all of Windows Server, and across all of our server applications. […]“. The PowerShell will probably also define the way to go for other companies developing for the Microsoft plattform.
And the good news: A properly designed commandline application is ideally prepared for PowerShell. The various combinations of command line arguments will just become different overloads/functions of the commandlet. No service application or job can do that.
Bits&Bytes
As it has become my custom (share both, bits and opinion) I have decided to share my own little console framework. (After the intro you wouldn’t have suspected I have one, would you ;-)?). It’s provided as is and it’s free, just mention my name and perhaps tell me of your extensions.
The framework is tailored for ease of use with applications that have more or less complex argument lists. You wouldn’t need it with trivial EXEs with a trivial fixed argument set and no useability demand (although I would argue that such applications do not exist). It also does not aim to fullfill academic pretensions, i.e. I refrained from trying to develop the last-framework-you-need that is fully metadata driven, aiming to solve all problems and doing it with 72 UML designed classes far to complex for the usual task at hand. I like the 80% approach and usually it fullfills 100% of my demands.
In this case the demands where primarily:
- Handle command line parsing
- Handle user feedback (including logo and help messages)
- Play a little with colored output (which is now readily supported with .NET 2.0)
Here’s a short sample that should give you an idea of how to use it:
- Provide a ressource file with messages:
<?xml version=″1.0″ encoding=″utf-8″ ?>
<root>
<data name=″Logo″>
<value>XCopy test application based on AJ.Console.ConsoleApp (c) by Alexander Jung – mailto:info@Alexander-Jung.NET</value>
</data>
<data name=″Syntax″>
<value>SYTNAX: XCopy.exe Source [Target] [/A | /M] [/EXCLUDE file1 [file2] [file3]. . . ]</value>
</data>
<data name=″Help″>
<value> This is just a simulation, no harm is done 😉</value>
</data>
</root>
- Derive from a given base class and overwrite three methods (argument parsing, switch parsing and processing):
class XCopyApp: ConsoleApp
{
[STAThread]
static int Main(string[] args)
{
XCopyApp app= new XCopyApp();
return app.Run(args);
}
protected override void ApplyArguments(string[] values)
{
// on argument is mandatory, the second optional
EnsureLength(values, 1, 2, null);
}
protected override void ApplySwitch(string name, string[] values)
{
switch (name.ToLower())
{
case ″/a″:
case ″/m″:
// no additional arguments
EnsureLength(values, 0, 0, name);
break;
case ″/exclude″:
// at least one additional argument
EnsureLength(values, 1, int.MaxValue, name);
break;
default:
// we don’t like what we don’t know
ThrowUnknownSwitch(name);
break;
}
} protected override void Process()
{
// just print out some of the arguments intention
string[] args= GetArguments();
WriteLine(″about to copy the following files: ″+args[0]);
if (args.Length>1)
WriteLine(″target is: ″+args[1]);
if (HasSwitch(″/a″) || HasSwitch(″/A″))
WriteLine(″only if archive bit is set, leaves the bit as is.″);
if (HasSwitch(″/m″) || HasSwitch(″/M″))
WriteLine(″only if archive bit is set, clears the bit afterwards.″);
}
}
- And the output looks like (yet more colorfull):
[D:ProjektePrivAJ.ConsoleXCopybinDebug]xcopy XCopy test application based on AJ.Console.ConsoleApp (c) by Alexander Jung - mailto:info@Alexander-Jung.NET Invalid number of argument: arguments needs at least 1 value(s). Use /? for further information. [D:ProjektePrivAJConsoleXCopybinDebug]XCopy.exe /? XCopy test application based on AJ.Console.ConsoleApp (c) by Alexander Jung - mailto:info@Alexander-Jung.NET SYTNAX: XCopy.exe Source [Target] [/A | /M] [/EXCLUDE file1 [file2] [file3]...] System parameter: @parameterfile read arguments from file !logfile write log file /?|/h[help] show help /v[erbose] verbose output /q[uiet] no output /nologo no logo This is just a simulation, no harm is done ;-) [D:ProjektePrivAJ.ConsoleXCopybinDebug]xcopy *.* x: /M /nologo about to copy the following files: *.* target is: x: only if archive bit is set, clears the bit afterwards.
And finally the code (downlad and rename to *.zip).
That’s all for now folks,
AJ.NET
Hi Alex,
I’m disappointed of you 😉 – because you don’t read my blog. If so, you had recognized that I wrote a same coloured article. In that article I offer a console framework that uses delegates to process the commandline arguments.
Perhaps we can combine your XML description with my commandline parsing framework.
But for now, have a nice vacation in Canada.
Cheers
Gerhard
http://jachman.wordpress.com/2006/05/05/framework-for-console-applications-or-how-to-parse-command-line-arguments/
Comment by Gerhard Stephan — September 1, 2006 @ 7:55 am
Re: your parameter parser.
You might enjoy writing a PowerShell cmdlet. Like your ConsoleApp idea, a cmdlet author extends our Cmdlet class, and gets a ton of stuff for free. For example, you simply add public properties to your class to define parameters. When you tag them with some of our attributes, you get validation, default values, etc.
Lee
Comment by Lee — September 2, 2006 @ 8:48 am
Gerhard, I’m so sorry :(. Every morning during my vacation my first thought was… no actually it was… come to think of it, I never really thought of you… 😀
(Note for other readers: Gerhard and I know each other.)
But it proves my point (first sentenses), doesn’t it?
Anyway, I’m still using this lib, yet plain console stuff is actually not on my allways too long things-to-look-into-list. Any effort in this area will probably be spent on PowerShell (perhaps in conjunction with MMC and/or MSBuild).
AJ.NET
Comment by ajdotnet — September 24, 2006 @ 3:53 pm