[Mono-list] Scripting the Linux OS with Mono
Elliott Draper
el at eldiablo.co.uk
Fri Nov 11 14:34:51 EST 2005
Hi Abe,
Abe Gillespie wrote:
>*snip
>mono script_host.exe MyScript.cs myarg1 myarg2
>
>*snip
>Has anyone out there done something or started a project like this?
>
>
Give the attached source code a try :-) I'm not claiming it's perfect,
but it's something I knocked up while I was bored this afternoon, and
you may be able to use it or tweak it to what you need. It uses the
features of CodeDom, available both in .Net and Mono, to compile the
source file to an in-memory assembly, where the code can then be
executed. The file "MonoScript.cs" can be compiled on its own to provide
the actual script execution tool, and also attached is a sample "script"
file ("Test.cs") that you can use to give it a bash. Simply compile the
MonoScript tool:
mcs MonoScript.cs
And then it's as easy as running:
mono MonoScript.exe --sourceFile:Test.cs
If for example your source file has a reference to System.Data then you
can simply run:
mono MonoScript.exe --sourceFile:Test.cs --references:System.Data.dll
For the full usage of the program, run:
mono MonoScript.exe --displayUsage:true
Any arguments you specify that MonoScript doesn't recognize will be
forwarded onto the "script" application, you can see this by appending
as many arguments as you want to the test script:
mono MonoScript.exe --sourceFile:Test.cs test1 test2 test3
Have a play, and give me a shout if you run into any bugs or problems
with it and I'll do my best to fix them, failing that, jump right in and
tweak the code yourself ;-)
If anyone else finds the code useful, let me know and I'll get the
entire thing (project files, a build file maybe, a bigger testsuite
perhaps?) up on my website as a package. Likewise if anyone has any
comments, let me have 'em!
>-Abe
>
>
Cheers,
-= El =-
>On 11/10/05, Kornél Pál <kornelpal at hotmail.com> wrote:
>
>
>>Hi,
>>
>>You have to compile C# code using mcs that will result in a .exe assembly
>>that can be executed using mono:
>>
>>$mcs some.cs
>>$mono some.exe
>>
>>If you prefer you can write a shell script that will do this as a single
>>step.
>>
>>Kornél
>>
>>----- Original Message -----
>>From: "Abe Gillespie" <abe.gillespie at gmail.com>
>>To: "MonoList" <mono-list at lists.ximian.com>
>>Sent: Friday, November 11, 2005 1:02 AM
>>Subject: [Mono-list] Scripting the Linux OS with Mono
>>
>>
>>Hey all,
>>
>>I was wondering if there's any easy way to run C# scripts in Mono.
>>I'm fairly new to Linux (just at about a year) and I'd like to avoid
>>learning yet another language (Perl, sh, etc.). Has anyone written a
>>.Net program that takes a file as input and runs that code? Perhaps
>>Mono can do this natively? How cool would it be to have startup
>>scripts written in C#?!
>>
>>Thanks for the help as always.
>>-Abe
>>_______________________________________________
>>Mono-list maillist - Mono-list at lists.ximian.com
>>http://lists.ximian.com/mailman/listinfo/mono-list
>>
>>
>>
>>
>_______________________________________________
>Mono-list maillist - Mono-list at lists.ximian.com
>http://lists.ximian.com/mailman/listinfo/mono-list
>
>
-------------- next part --------------
using System;
public class MonoScriptTest
{
//This simply displays the amount of arguments passed into it
public static void Main(string[] args)
{
Console.WriteLine(string.Format("MonoScript: test script, {0} arguments",args.Length.ToString()));
}
}
-------------- next part --------------
using System;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.Collections.Specialized;
using System.Reflection;
namespace MonoScript
{
/// <summary>
/// This allows the execution of a .cs file as if it were a program, compiling it on the fly then executing it
///
/// Example syntax:
///
/// MonoScript /s:myscript.cs /r:System.Drawing.dll This would execute the code within the
/// file myscript.cs, and reference System.Drawing.dll when compiling it
///
/// MonoScript /s:myscript.cs /r:System.Data.dll /n:true /v:true This would compile the code within the
/// file myscript.cs, reference System.Data.dll when doing it, stop the MonoScript logo from displaying
/// when run, and also ensure that ONLY compilation takes place, not execution, for validation purposes
/// </summary>
class MonoScript
{
/// <summary>
/// These are our options for the script to execute
/// </summary>
static MonoScriptOptions Options;
/// <summary>
/// Main entry point
/// </summary>
/// <param name="args"></param>
static void Main(string[] args)
{
//Create our options from the command line args
Options = new MonoScriptOptions(args);
//Only show the logo if we haven't been told not to
if(!Options.NoLogo)
Logo();
//If the user has requested the program usage, then show it here
if (Options.DisplayUsage)
{
Usage();
return;
}
//This will hold our in-memory compiled assembly
Assembly assembly = null;
try
{
//Compile our script to an assembly
assembly = CompileFile();
//Ensure we have an assembly
if (assembly == null)
throw new Exception("No assembly generated!");
}
catch (Exception ex)
{
//Display any errors found while compiling
Console.WriteLine(string.Format(
"Exception caught while compiling script:{0}{1}{0}",
Environment.NewLine,
ex.ToString()));
return;
}
//If we are only validating (compiling) the script, then exit here
if (Options.ValidateOnly)
{
Console.WriteLine("Validation complete");
return;
}
try
{
//Execute the script
ExecuteAssembly(assembly);
}
catch (Exception ex)
{
//Catch any errors while executing it
Console.WriteLine(string.Format(
"Exception caught while executing script:{0}{1}{0}",
Environment.NewLine,
ex.ToString()));
return;
}
}
/// <summary>
/// This outputs the program logo
/// </summary>
static void Logo()
{
Console.WriteLine("MonoScript: (c) 2005, Elliott Draper <el at eldiablo.co.uk>");
}
/// <summary>
/// This outputs the usage of the app
/// </summary>
static void Usage()
{
Console.WriteLine("Usage: MonoScript --sourceFile:[VALUE] [ARGS] where [ARGS] can optionally be one of the following:");
Console.WriteLine("--references:[VALUE], -r:[VALUE], /r:[VALUE] : any references needed to compile the script");
Console.WriteLine("--noLogo:[VALUE], -n:[VALUE], /n:[VALUE] : stops from showing the MonoScript logo");
Console.WriteLine("--displayUsage:[VALUE], -d:[VALUE], /d:[VALUE] : displays this usage info");
Console.WriteLine("--validateOnly:[VALUE], -v:[VALUE], /v:[VALUE] : validates the script by compiling it, but doesn't execute it");
}
/// <summary>
/// This compiles the specified file into an assembly, exceptioning on error
/// </summary>
/// <returns></returns>
static Assembly CompileFile()
{
//Setup the parameters
CompilerParameters parameters = new CompilerParameters();
foreach (string reference in Options.References)
parameters.ReferencedAssemblies.Add(reference);
parameters.GenerateInMemory = true;
//Create the provider and compiler
CodeDomProvider provider = new Microsoft.CSharp.CSharpCodeProvider();
if (provider == null)
throw new Exception("Cannot create C# code provider!");
ICodeCompiler compiler = provider.CreateCompiler();
if (compiler == null)
throw new Exception("Cannot create C# compiler!");
//Compile the file with the specified parameters
CompilerResults results = compiler.CompileAssemblyFromFile(
parameters,
Options.SourceFile);
//Grab and validate the results
if (results == null)
throw new Exception("Could not retrieve results from compilation!");
if (results.Errors.HasErrors)
throw new Exception(results.Errors[0].ErrorText);
//Return the compiled assembly
return results.CompiledAssembly;
}
/// <summary>
/// This executes the specified compiled assembly
/// </summary>
/// <param name="assembly"></param>
static void ExecuteAssembly(Assembly assembly)
{
//Invoke the entry point found for the assembly, with the specified arguments
GetEntryPoint(assembly).GetMethod("Main").Invoke(null, new object[1] { Options.ScriptArgs });
}
/// <summary>
/// This attempts to locate a type with a static "Main" method within the generated script assembly
/// </summary>
/// <param name="assembly"></param>
/// <returns></returns>
static Type GetEntryPoint(Assembly assembly)
{
//Loop through all of the generated types
foreach (Type type in assembly.GetTypes())
{
//If we find one that has a static Main method, lets return it
if (type.GetMethod("Main") != null && type.GetMethod("Main").IsStatic)
return type;
}
//If we get to here, we got no entry point :-(
throw new Exception("Unable to find a class with a valid entry point within the script!");
}
}
/// <summary>
/// This contains options for calling MonoScript with
/// </summary>
class MonoScriptOptions
{
#region Private Variables
private string[] _references = new string[0];
private string _sourceFile;
private bool _noLogo = false;
private bool _displayUsage = false;
private bool _validateOnly = false;
private StringCollection optionArgs = new StringCollection();
#endregion
#region Properties
/// <summary>
/// This specifies that no logo should be shown for the MonoScript app
/// </summary>
public bool NoLogo
{
get
{
return _noLogo;
}
}
/// <summary>
/// This specifies that the MonoScript usage should be displayed
/// </summary>
public bool DisplayUsage
{
get
{
return _displayUsage;
}
}
/// <summary>
/// This specifies that the source file shouldn't be executed as a script; just that it should be
/// compiled and validated to ensure it is a valid source file
/// </summary>
public bool ValidateOnly
{
get
{
return _validateOnly;
}
}
/// <summary>
/// Any assembly references needed to compile the script
/// </summary>
public string[] References
{
get
{
return _references;
}
}
/// <summary>
/// The source file acting as the script
/// </summary>
public string SourceFile
{
get
{
if(_sourceFile==null)
throw new Exception("No source file specified!");
return _sourceFile;
}
}
/// <summary>
/// The arguments to call the script with
/// </summary>
public string[] ScriptArgs
{
get
{
string[] args = new string[optionArgs.Count];
for (int i = 0; i < args.Length; i++)
args[i] = optionArgs[i];
return args;
}
}
#endregion
#region Constructor
/// <summary>
/// Default constructor
/// </summary>
public MonoScriptOptions(string[] args)
{
//Validate
if(args==null||args.Length==0)
throw new Exception("No arguments specified!");
//Add in args
optionArgs.AddRange(args);
//Fill out options
_sourceFile = GetArgument("sourceFile");
string noLogo = GetArgument("noLogo");
_noLogo = (noLogo != null && noLogo.ToLower() == "true") ? true : false;
string displayUsage = GetArgument("displayUsage");
_displayUsage = (displayUsage != null && displayUsage.ToLower() == "true") ? true : false;
string validateOnly = GetArgument("validateOnly");
_validateOnly = (validateOnly != null && validateOnly.ToLower() == "true") ? true : false;
string references = GetArgument("references");
if(references!=null)
_references = references.Split(Convert.ToChar(","));
}
#endregion
#region Methods
/// <summary>
/// This retrieves an argument value from the list of arguments
/// If it finds a requested argument, it then removes it from the list of arguments
///
/// If you request the argument for the command "references", any argument such as:
/// --references:[VALUE]
/// -r:[VALUE]
/// /r:[VALUE]
///
/// will all result in the returning of "[VALUE]"
/// If more than one is specified, they will be taken in the above order
/// </summary>
/// <param name="requestedCmd"></param>
/// <returns></returns>
private string GetArgument(string requestedCmd)
{
//These variables are used throughout
string value = null;
string arg = null;
//Loop through all arguments
for(int i=0;i<optionArgs.Count;i++)
{
arg = optionArgs[i];
//Prepare the first format of the requested command to search for
string cmd = string.Format("--{0}:", requestedCmd);
if (arg.IndexOf(cmd) != -1)
{
//If it's found, then set the value, and break the loop
value = arg.Substring(
0 + cmd.Length,
arg.Length - cmd.Length);
break;
}
//Try the next format of the requested command
cmd = string.Format("-{0}:", requestedCmd[0]);
if (arg.IndexOf(cmd) != -1)
{
//Again, if found, set the value and break out of the loop
value = arg.Substring(
0 + cmd.Length,
arg.Length - cmd.Length);
break;
}
//Finally try the last format of the requested command
cmd = string.Format("/{0}:", requestedCmd[0]);
if (arg.IndexOf(cmd) != -1)
{
//Set value, break loop
value = arg.Substring(
0 + cmd.Length,
arg.Length - cmd.Length);
break;
}
}
//If we have got here with a value set, then we found our argument value, so remove the argument,
//and return our value
if (value != null)
{
optionArgs.Remove(arg);
return value;
}
//Otherwise, return null
return null;
}
#endregion
}
}
More information about the Mono-list
mailing list