Just to try out this idea I wrote out CSBat.cs, which allows C# code in bat and cmd files.
Here is the problem this solves: Frequently I just want to write a short C# snippet. I don't want to write it as a source file, compile it, and run it. I'd rather just put the snippet in a bat file and run the bat file. I want to shorten the code-compile-execute loop to just code-execute. CSBat solves this problem. (Note: The in-memory compiler code was taken from Don Box's CSRepl from his blog. Also you will need the Whidbey 2.0 runtime to play with this sample.)
Here is how to use the sample:
- Save the hello.cmd code below into hello.cmd
- Save the csbat.cs code into csbat.cs
- Type: csc.exe csbat.cs /r:System.dll
- Type: hello.cmd
- The hello.cmd script should print "Hello...". Now edit the script and run it again. It should run with the changes.
Here are the contents of hello.cmd:
@echo off
csbat.exe Chk %~f0 %*
goto :EOF
#! -*- csharp script start -*-
using System;
public class Chk
{
public static void Main(string[] args)
{
Console.WriteLine("Hello world from CSBat.");
Console.WriteLine("Argument count was: " + args.Length);
foreach (string arg in args)
{
Console.WriteLine("arg = " + arg);
}
}
}
Here are the contents of csbat.cs:
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.IO;
using System.CodeDom;
using System.CodeDom.Compiler;
namespace CSBat
{
class Program
{
static string Eval(string program, string className, string[] args)
{
ICodeCompiler compiler = CodeDomProvider.CreateProvider("C#").CreateCompiler();
CompilerParameters cp = new CompilerParameters();
cp.GenerateExecutable = false;
cp.GenerateInMemory = true;
CompilerResults results = compiler.CompileAssemblyFromSource(cp, program);
if (results.Errors.HasErrors)
{
throw new ApplicationException(results.Errors[0].ErrorText);
}
else
{
Assembly assembly = results.CompiledAssembly;
Type target = assembly.GetType(className);
MethodInfo method = target.GetMethod("Main");
object[] parameters = new object[]{ args };
object result = method.Invoke(null, parameters);
return result == null ? null : result.ToString();
}
}
static void Main(string[] args)
{
try
{
if (args.Length < 2) { throw new ArgumentException("Usage: csbat CLASS FILE ARGS"); }
string className = args[0];
string filePath = args[1];
List<string> argList = new List<string>(args);
argList.RemoveAt(0);
argList.RemoveAt(0);
args = argList.ToArray();
string fileContents = File.ReadAllText(filePath);
fileContents =
Regex.Replace(fileContents, "^.*?\n#![^\n]*csharp script[^\n]*\n", "", RegexOptions.Singleline);
string result = Eval(fileContents, className, args);
}
catch(Exception e)
{
Console.WriteLine("Error: " + e.Message);
}
return;
}
}
}