LINQ可以在PowerShell中使用吗?

时间:2016-07-13 19:52:41

标签: linq powershell

我正在尝试在PowerShell中使用LINQ。看起来这应该是完全可能的,因为PowerShell是建立在.NET Framework之上的,但我无法让它工作。例如,当我尝试以下(人为的)代码时:

$data = 0..10

[System.Linq.Enumerable]::Where($data, { param($x) $x -gt 5 })

我收到以下错误:

  

无法找到" Where"和参数计数:" 2"。

不要介意这可以通过Where-Object完成。这个问题的关键不是找到在PowerShell中执行此操作的惯用方法。如果我可以使用LINQ,那么在PowerShell中,一些任务可以轻松完成。

5 个答案:

答案 0 :(得分:43)

您的代码存在的问题是,PowerShell无法决定应该投射ScriptBlock实例({ ... })的特定委托类型。 因此,它无法为Where method的通用第二个参数选择类型具体的委托实例化。并且它也没有明确指定通用参数的语法。要解决此问题,您需要自己将ScriptBlock实例强制转换为正确的委托类型:

$data = 0..10
[System.Linq.Enumerable]::Where($data, [Func[object,bool]]{ param($x) $x -gt 5 })
  

为什么[Func[object, bool]]有效,但[Func[int, bool]]没有?

因为您的$data[object[]],而不是[int[]],因为PowerShell默认会创建[object[]]数组;但是,您可以明确地构建[int[]]个实例:

$intdata = [int[]]$data
[System.Linq.Enumerable]::Where($intdata, [Func[int,bool]]{ param($x) $x -gt 5 })

答案 1 :(得分:20)

使用更广​​泛的答案来补充PetSerAl's helpful answer以匹配问题的通用标题:

注意:直接对LINQ的支持 - 语法与C#中的语法相当 - 正在讨论未来版本的PowerShell Core < / {>在this GitHub issue。 功能

在PowerShell中使用LINQ

  • 您需要 PowerShell v3或更高版本

  • 您不能直接在集合实例上调用LINQ扩展方法,而是必须以[System.Linq.Enumerable]类型静态方法调用LINQ方法您将输入集合作为第一个参数传递。

    • 必须这样做会消除LINQ API的流动性,因为方法链不再是一种选择。相反,您必须以相反的顺序嵌套静态调用

    • 例如,您必须写$inputCollection.Where(...).OrderBy(...)

    • 而不是[Linq.Enumerable]::OrderBy([Linq.Enumerable]::Where($inputCollection, ...), ...)
  • 帮助程序功能和类:

    • 某些方法(例如.Select())具有接受通用 Func<>委托的参数(例如,Func<T,TResult>可以使用PowerShell代码创建,通过强制转换应用于脚本块;例如:
      [Func[object, bool]] { $Args[0].ToString() -eq 'foo' }

      • Func<>委托的第一个泛型类型参数必须与输入集合的元素类型相匹配;请记住,PowerShell默认创建[object[]]数组。
    • 某些方法(例如.Contains().OrderBy)的参数接受实现特定接口的对象,例如IEqualityComparer<T>和{{1 }};此外,输入类型可能需要实现IComparer<T>才能使比较按预期工作,例如IEquatable<T>;所有这些需要编译类编写,通常是在C#中编写的(尽管您可以通过将带有嵌入式C#代码的字符串传递给.Distinct() cmdlet来从PowerShell创建它们) ;但是,在 PSv5 + 中,您也可以使用自定义PowerShell classes,但有一些限制

  • 通用方法

    • 某些LINQ 方法本身是通用的,因此需要一个类型参数; PowerShell不能直接调用此类方法,而必须使用反射; e.g:

      Add-Type
  • LINQ方法返回迭代器 而不是实际的集合。

    • 但是,在许多情况下,您将能够像使用集合一样使用迭代器,例如通过管道发送它时。

      • 但是,您无法通过调用# Obtain a [string]-instantiated method of OfType<T>. $ofTypeString = [Linq.Enumerable].GetMethod("OfType").MakeGenericMethod([string]) # Output only [string] elements in the collection. # Note how the array must be nested for the method signature to be recognized. > $ofTypeString.Invoke($null, (, ('abc', 12, 'def'))) abc def 来获取结果计数,也不能索引到迭代器中;但是,您可以使用成员枚举。
    • 如果需要将结果作为静态数组来获取通常的集合行为,请将调用包装在 .Count 中。

      • 存在返回不同数据结构的类似方法,例如[Linq.Enumerable]::ToArray(...)

对于高级示例,请参阅我的this answer 有关所有LINQ方法的概述,包括示例,请参阅this great article

简而言之:使用来自PowerShell的LINQ非常麻烦 仅值得付出努力以下任何一项适用:

  • 您需要PowerShell的cmdlet无法提供的高级查询功能
  • 效果至关重要 - 请参阅this article

答案 2 :(得分:6)

如果要实现类似LINQ的功能,那么PowerShell具有一些cmdlet和功能,例如:Select-ObjectWhere-ObjectSort-ObjectGroup-Object。它具有适用于大多数LINQ功能(如投影,限制,排序,分组,分区等)的cmdlet。

请参阅link

有关更多详细信息,此link可能会有所帮助。

答案 3 :(得分:0)

当我想在PowerShell中进行稳定排序时,我遇到了LINQ(稳定:如果要排序的属性在两个(或更多)元素上具有相同的值:保留其顺序)。 Sort-Object具有-Stable-Switch,但仅适用于PS 6.1+。而且,.NET中的通用集合中的Sort()-实现不稳定,因此我遇到了LINQ,在该文件中文档说它很稳定。

这是我的(测试-)代码:

# Getting a stable sort in PowerShell, using LINQs OrderBy

# Testdata
# Generate List to Order and insert Data there. o will be sequential Number (original Index), i will be Property to sort for (with duplicates)
$list = [System.Collections.Generic.List[object]]::new()
foreach($i in 1..10000){
    $list.Add([PSCustomObject]@{o=$i;i=$i % 50})
}

# Sort Data
# Order Object by using LINQ. Note that OrderBy does not sort. It's using Delayed Evaluation, so it will sort only when GetEnumerator is called.
$propertyToSortBy = "i" # if wanting to sort by another property, set its name here
$scriptBlock = [Scriptblock]::Create("param(`$x) `$x.$propertyToSortBy")
$resInter = [System.Linq.Enumerable]::OrderBy($list, [Func[object,object]]$scriptBlock )
# $resInter.GetEnumerator() | Out-Null

# $resInter is of Type System.Linq.OrderedEnumerable<...>. We'll copy results to a new Generic List
$res = [System.Collections.Generic.List[object]]::new()
foreach($elem in $resInter.GetEnumerator()){
    $res.Add($elem)
}

# Validation
# Check Results. If PropertyToSort is the same as in previous record, but previous sequence-number is higher, than the Sort has not been stable
$propertyToSortBy = "i" ; $originalOrderProp = "o"
for($i = 1; $i -lt $res.Count ; $i++){
    if(($res[$i-1].$propertyToSortBy -eq $res[$i].$propertyToSortBy) -and ($res[$i-1].$originalOrderProp -gt $res[$i].$originalOrderProp)){
        Write-host "Error on line $i - Sort is not Stable! $($res[$i]), Previous: $($res[$i-1])"
    }
}

答案 4 :(得分:0)

通过在Linq名称空间中设置using语句,可以使Linq链接更加流畅。然后,您可以直接调用where函数,而无需调用静态Where函数。

using namespace System.Linq
$b.Where({$_ -gt 0})

$ b是字节数组,我想获取所有大于0的字节。

完美的作品。