命令行上的 LINQ






4.67/5 (4投票s)
是否想从 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”(请参阅示例项目的调试设置)。很酷
一如既往,安全编码!