65.9K
CodeProject 正在变化。 阅读更多。
Home

命令行上的 LINQ

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.67/5 (4投票s)

2012年5月4日

CPOL

1分钟阅读

viewsIcon

41929

downloadIcon

363

是否想从 powershell 或命令行运行 Linq 语句?

引言

你是否希望在 PowerShell 或命令行中运行 .linq 脚本,并完全采用函数式编程? Linqoott 是一个控制台 LINQ 执行引擎。它读取 .linq 文件并将输出转储到控制台。

使用方法

首先,我们从第一个参数获取脚本源代码:

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

确定 LINQ 的“类型”……(语句/表达式)

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

它是单个表达式还是语句(多行),这会影响你将脚本格式化为代码块的方式。

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

有了脚本,就必须执行它。这并不难,将脚本嵌入到模板 C# Main() 中即可。它看起来像这样:

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; }", 
"}"};

嵌入在那里的“Dump”方法是为了帮助输出语句中的值。

接下来,我们“编译”源代码。我不能声称在这里有功劳,请参阅 Code Project 文章:动态代码集成

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;
}

所以我们有了这个模糊的“Assembly”对象……我们该如何处理它?现在是时候反思了。不!不是反思你的编码技能,而是 System.Reflection,你这个黑客猴子!

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); 
}

就这样!你可以运行一个脚本……

示例胜过千言万语

这是一个 LINQ 脚本示例。它枚举你的 'C:\program files' 目录,并返回所有主文件版本大于 10 的 .exe 文件,并按次要版本排序。一个有点傻的例子,但它可以测试代码。

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))

让我们执行它……在我的机器上,它返回

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 
... 

等等?!“c:\foo\...”这是从哪里来的?嗯,我添加了参数。请看这个

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

它将“c:\program files”替换为第一个传递的命令行参数,在本例中为“c:\foo”(请参阅示例项目的调试设置)。很酷 Wink | ;)

一如既往,安全编码!

© . All rights reserved.