Tuesday, June 30, 2009

Scriptster: C# as a scripting language

I love Python! Unlike the vast majority of script languages that evolved ad-hoc, Python was built in a controlled way and has features, syntax, and runtime which click together.

I learned it at Google, got readability in it (http://1-800-magic.blogspot.com/2008/01/after-8-months-no-longer-noogler.html, and have written a few thousand lines of code since. I must say that of all the scripting languages, Python is probably the most amenable to writing thousand-plus line programs (maybe with the exception of Ruby).

As far as I am concerned, it would be nice if all other command interpreter scripting died (sorry, PowerShell) and Python were integrated into shells everywhere.

There is one problem with Python, however - it is yet another language to learn, and yet another development/runtime environment to maintain. Its integration with Windows is good, but not nearly as good as C# which was literally made for Windows. And so after I came back to Microsoft (http://1-800-magic.blogspot.com/2008/06/back-to-microsoft.html) and found myself doing most things in C# and C++, I started getting more and more rusty with Python.

Of course, being a dev manager does not help - my opportunities to code are few and far between.

What that meant that scripting became harder and harder. Soon I found myself writing small executables in C# instead of scripts. Which was all good, except that you end up with two things - a source file and an executable, which now need to be maintained together, checked in together, etc. And for very small things, it's a considerable overhead.

And then I had an idea. .NET, you see, ships with a C# compiler in the box - it is present on every (updated) Windows system. What if I were to write a small program, a C# "interpreter" of sorts, that would run C# programs directly from a command line as if they were batch files?

And so Scriptster was born.

Scriptster is a single executable that allows you to run C# programs directly, without manually compiling them. It automatically compiles C# code before running (and caches the compiled versions so the next execution is faster), but to the user it is completely transparent. It is fast, too - even the first, compiling, invocation takes fractions of a second. Subsequent runs are instantaneous.

Scripter is copy-deployable: simply copy it into a directory of your choice, run
    scriptster --install
(from a command prompt started with administrator privileges - "Run as Administrator"), and close and reopen CMD windows where you expect to be using it (CMD needs update to PATHEXT environment variable, which it reads on load).

After this, you can author and run C# scripts. For example, open Notepad, and type the following:
using System;

class Program
{
static void Main(string[] args)
{
Console.WriteLine("This program was invoked with the following command line parameters:");
foreach(string s in args)
Console.WriteLine("{0}", s);
}
}
Save this file as testscript.csscript (the files need to have .csscript extension to work with Scriptster), and you can run it directly from the command line:
    c:\temp> testscript Blah blah blah
This program was invoked with the following command line parameters:
Blah
blah
blah
You can run considerably more involved scripts with Scriptster of course, in fact, any C# program can be run, as long as it is (1) in one file, and (2) only relies on assemblies in GAC.

Here's an example of a program that queries LDAP for user alias:

//#ref System.DirectoryServices.dll
using System;
using System.Collections.Generic;
using System.DirectoryServices;
using System.Text;

class Program
{
static void Main(string[] args)
{
DirectorySearcher ds = new DirectorySearcher();

ds.PropertiesToLoad.Add("mail");

foreach (string alias in args)
{
ds.Filter = "(SAMAccountName=" + alias + ")";
SearchResult result = ds.FindOne();
if (result == null)
{
Console.Error.WriteLine("Could not resolve {0}", alias);
continue;
}
Console.WriteLine("{0}'s email is {1}", alias,
result.Properties["mail"][0].ToString());
}
}
}
Notice "//#ref" at the top of the file? This is how you tell Scriptster about referenced assembly.

You can edit scripts in Visual Studio, debug them, then rename the files to .csscript extensions and run them right from the command line. You can take existing small programs, and run them from the command line, too.

Interested? You can download Scriptster, including the source code, from CodePlex: http://scriptster.codeplex.com.

Give it a whirl, and let me know how it works out.

6 comments:

Yuri Chebotarev said...

sweet. suggestion - its annoying to write
using System;
......
class Program
{
static void Main(string[] args)
{
every time... introduce some kind of "light" syntax).

DzembuGaijin said...

Nice! I wonder why Microsoft did not made it build in :-) ( and made some more of these security holes :-) Kidz love scriptz to pw0n!

My 2 ( actually 5 cents ) :-)

1) It will be nice to have option if it will be just script: just BODY of what is inside Main. As Yuri suggest, just allow to skip this part: for small sweet scripts this is overhead.

2) Yes, it is hard, but 'nuke' your //ref thing :-) Make some dictionary and scan for "signs" and add "using" automatically. I believe if you "add" too much it will not be too bad :-)

3) It should run on any platform. Make it work with "mono" :-) and help "spread" to Linux and OS X - even if % is small it will surely help get PR and traction :-) and will boost Windows adoption.

4) Of course, documentation, "C# for dummies and examples are in order on "dedicated site".

5) I would call it SharpScript or some thing :-) and get Web site with some community engine. It may just point to codeplex. if you like :-) but I would add some extra staff :-)

nathan3700 said...

Sounds interesting. If I ever get assimilated by the Windows world, I see about using it. I know it is old school, but I use Perl for quick jobs. I work on Unix, but at home I use Windows and have Cygwin unix environment.

I really hate the Windows "cmd" command line windows. I like to run a Cygwin rxvt window running Bash as the shell. What do you windows guys use for a command line?

Sergey Solyanik said...

We use CMD. I actually don't think CMD is any different from bash/csh/ksh in terms of usability and programmability.

As far as using them all w/o scripting, it's just what you're used to.

As far as programming, I submit that Unix shells are even worse - does this look like a statement from a reasonable programming language to you?

if [! -x $HOME/blah ]; then...

(1) Why ";" after conditional?
(2) Why these silly conditionals which are the cross between Fortran and command line flags?
(3) Why existence of a variable and the emptiness of the variable are different tests, even though you cannot have an empty variable?
...

Yuri Chebotarev said...

>We use CMD.
Why not PowerShell?

Sergey Solyanik said...

Oh, I don't know. It's new, not much penetration yet. It doesn't integrate into CMD yet. I personally am not a huge fan of PowerShell syntax.

I think a lot of management scripting interface at Microsoft is done via PowerShell now, though.