.NET Console on the Console
Monday, November 14, 2005
I've started work on console application utilities, with a console app infrastructure as the ultimate goal in mind. Every console app should have easy access to the its environment, to the command line, including simple validation facilities. You can see how far I have progressed in the classes in Gregor.Core.Configuration.
Further ideas: any console application should be extensible with AddIns - the need is appearant, for example, in an application like the TextGen tool. Then, every console application should be scriptable as well. Again, TextGen is the perfect example.
The other day, I was thinking about how to best create a little tool that extracts links from an web document and downloads the target ressources to local disc. What would be the best way to structure the application? Should parsing be separate from the downloading code? How would such an application relate to my existing Downloader tool (which is part of the File Tools collection, available off the Downloads page), which so far is FTP-only?
I did not find satisfactory answers to these questions. One thing that made sense to me was to place the link extraction code, in a somewhat generalized way, into the Gregor.Core library. But the problem of how to factor (the word is a back formation from the verb refactor) the various pieces of functionality remained. UNIX makes it easy to for console apps to work together, and use shell scripts as glue. The Windows approach is to place things into DLLs, and have an application glue them together.
So how did I solve my problem? Given the following facts:
- I need well-factored pieces of functionality to exist in libraries.
- Things need to be glued together in an application. But since the scope of the link-extractor/downloader application is still unclear to me, I'm leaning towards scripting.
- I do have a code interpreter that happens to play nicely with .NET class libraries - .NET Console. It also runs on the command line, and can be used to process code files unattendedly.
Therefore, I decided to implement my application as interpreted code:
// DownloadLinks.cs UserCommand.LoadAssembly("Gregor.Inet.dll"); UserImport.Add("System"); UserImport.Add("System.Net"); UserImport.Add("Gregor.Core"); UserImport.Add("Gregor.Inet"); UserOption.Const = false; cmdLine = UserCommand.GetCommandLine(); Check.ValueGreater(cmdLine.OperandCount, 0); var sContent = NetUtil.ReadUrl(cmdLine.GetOperand(0)); var links = Parse.ExtractBetweenAll(sContent, "href=\"", "\""); var wc = new WebClient(); downloadLink(sLink) { var sFileName = getFileName(sLink); var sMsg = string.Concat("Downloading ", sLink, " to ", sFileName, " ..."); Dev.Trace(sMsg); wc.DownloadFile(sLink, sFileName); } getFileName(var sLink) { var sRet = sLink; // ... this works only with relative links to files so far sRet; } foreach(var sLink in links){ downloadLink(sLink); }
The code can be invoked like this:
NetConsole /file:DownloadLinks.cs http://peisker.net/journal.htm
This approach can be generalized to satisfy the requirements of both extensible and scriptable console applications mentioned above as well - .NET Console can serve as a command interpreter (aka, shell), and, if you will, as a generalized console application infrastructure.