[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