Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / programming / tools

LINQ on the Command-Line

4.67/5 (4 votes)
4 May 2012CPOL1 min read 41.4K   331  
Ever want to run a Linq statement from powershell or command line?

Introduction

Have you ever wanted to run a .linq script on the power shell or command line and go all functional? Linqoott is a console LINQ execution engine.  It reads .linq files and dumps the output to the console. 

Using the Code   

First we get the script source from the first argument: 

C#
var isLinq = args[0].ToLower().EndsWith(".linq");

Find out the LINQ "kind"... (statement/expression)  

C#
string linqType = (isLinq)?XDocument.Parse(lins[0]).Root.Attribute("Kind").Value:string.Empty;

Is it a single expression or a statement (multiple lines), this effects how you format the script into a code block.

C#
var script = string.Format(("Statements" == linqType) ? "{0}\nreturn null;" : "return {0};",
    string.Join("\n", (isLinq) ? lins.Skip(2).Where(s => !s.TrimStart().StartsWith("//")) : lins));

Have a script, must execute.  Not as hard as you think, embed the script in a template C# Main().  It looks like this: 

C#
static readonly string[] tmplApp = new string[]{
"using System;",
"using System.Collections.Generic;",
"using System.Linq;",
"using System.Xml.Linq;",
"using System.Text;",
"using System.IO;",
"",
"class Script{ public static object Main(string[] args){{CODE}} }",
"",
"public static class ScriptExtensions",
"{",
"    public static object Dump(this object value)",
"    {",
"        if (null != value)",
"        {",
"            if (value is IEnumerable<object>)",
"                Console.WriteLine(string.Join(\"\\n\", (value as IEnumerable<object>).Select(s => s.Dump().ToString())));",
"            else",
"                Console.WriteLine(value);",
"        }",
"        return value;",
"    }",
"    public static object ToFile(this object value, string fileName) { File.WriteAllText(fileName, value.Dump()); return value; }", 
"}"};

The "Dump" method embedded in there is to help output the values in statements. 

Next we "compiled" the source.  I can't claim credit here see the Code Project Article: Dynamic Code Integration

C#
private static Assembly CompileSource(string sourceCode)
{
    CodeDomProvider cpd = new CSharpCodeProvider();
    CompilerParameters cp = new CompilerParameters() { GenerateExecutable = false };
    cp.ReferencedAssemblies.AddRange(new string[] { "System.dll", "System.Xml.dll", "System.Xml.Linq.dll", "System.Core.dll", "System.Data.dll" });
 
    // Invoke compilation.
    CompilerResults cr = cpd.CompileAssemblyFromSource(cp, sourceCode);
 
    return cr.CompiledAssembly;
}

So we have this vague "Assembly" object thingy... What do we do with it? This is a good time to reflect. NO! not on your coding skills, System.Reflection, you hack monkey!

C#
var scrObj = asm.CreateInstance("Script");
try{
  object result = scrObj.GetType().InvokeMember("Main", BindingFlags.InvokeMethod, null,scrObj,     	new object[] { args.Skip(1).ToArray() });
  if ("Statements" != linqType)
        result.Dump();
}
catch (Exception x)
{
    Console.WriteLine("Failed executing script:\n\n{0}\n\n{1}", script, x.Message); 
}

And that's it! You can run a script...

Samples Worth a Thousand Bugs

Here's a LINQ script sample. This enumerates your 'C:\program files' directory and returns all .exe's with a file major version greater than 10, and sorts by minor version. Silly example, but it exercises the code.

C#
Directory.GetFiles(@"c:\program files", "*.exe", SearchOption.AllDirectories)
 .Where(v=>v.FileMajorPart>10).OrderBy(v=>v.FileMinorPart)
 .Select(f=>f.FileName.Replace(@"c:\program files", args[0]))
 .ToArray().Select(f=>FileVersionInfo.GetVersionInfo(f))

Let's execute that ... On my machine it returns:

c:\foo\Common Files\logishrd\WUApp64.exe
c:\foo\Common Files\Microsoft Shared\DW\DW20.EXE 
c:\foo\Common Files\Microsoft Shared\DW\DWTRIG20.EXE
c:\foo\Common Files\Microsoft Shared\OFFICE14\MSOICONS.EXE
c:\foo\Common Files\Microsoft Shared\OFFICE14\MSOXMLED.EXE 
... 

Wait a minute?!  "c:\foo\..." where did that come from? Hmm, I added arguments. See this:

C#
.Select(f=>f.FileName.Replace(@"c:\program files", args[0]))

That replaces "c:\program files" with the first passed command-line argument,  in this case "c:\foo" (see sample project's debug settings). Pretty cool Wink | ;)   

As always, code safe!   

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)