【问题标题】:Can LINQ be used in PowerShell?LINQ 可以在 PowerShell 中使用吗?
【发布时间】:2022-03-11 07:38:18
【问题描述】:

我正在尝试在 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 中执行某些任务会容易很多。

【问题讨论】:

  • 对该问题的最高投票答案表明您不能在 PowerShell 中使用 LINQ 运算符stackoverflow.com/questions/2869967/…
  • 我觉得奇怪的是,你会对Where-Object 说“没关系”,只是转身谈论一种“惯用的”方式--Where-Object PowerShell 的事实上惯用的方式来做到这一点。如果您真的想要 LINQ(而不是类似 LINQ 的 cmdlet),我建议您研究二进制 cmdlet;这样,您就可以提交集合并在其应有的位置使用 LINQ(C# 或 VB.NET)。

标签: linq powershell


【解决方案1】:

您的代码的问题是 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 })

【讨论】:

    【解决方案2】:

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

    注意:以下内容至少适用于 PowerShell 7.2。 Direct 对 LINQ 的支持——其语法与 C# 中的语法相当——正在GitHub issue #2226 中讨论 PowerShell Core未来 版本。

    在 PowerShell 中使用 LINQ

    • 您需要 PowerShell v3 或更高版本

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

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

      • 例如,您必须写 [Linq.Enumerable]::OrderBy([Linq.Enumerable]::Where($inputCollection, ...), ...) 而不是 $inputCollection.Where(...).OrderBy(...)

    • 辅助函数和类:

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

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

    • 通用方法

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

        # 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.
        PS> $ofTypeString.Invoke($null, (, ('abc', 12, 'def')))
        abc
        def
        
      • 有关更详细的示例,请参阅this answer

    • LINQ 方法返回一个惰性枚举,而不是一个实际的集合;也就是说,返回的还不是实际数据,而是枚举时会产生数据的东西

      • 自动执行枚举的上下文中,特别是在管道中,您将能够像使用集合一样使用可枚举。

        • 但是,由于可枚举本身不是一个集合,因此您无法通过调用.Count 来获取结果计数,也无法index 进入迭代器;但是,您可以使用member-access enumeration(提取被枚举对象的属性值)。
      • 如果您确实需要将结果作为静态数组来获得通常的收集行为,请将调用包装在 [Linq.Enumerable]::ToArray(...) 中。

        • 存在返回不同数据结构的类似方法,例如::ToList()

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


    简而言之:在 PowerShell 中使用 LINQ 很麻烦,并且只有在满足以下任一条件时才值得努力

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

    【讨论】:

      【解决方案3】:

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

      Powershell One-Liners: Collections and LINQ

      有关使用 Linq 的更多详细信息以及如何使其更容易,文章 LINQ Through Powershell 可能会有所帮助。

      【讨论】:

        【解决方案4】:

        当我想在 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])"
            }
        }
        

        【讨论】:

          【解决方案5】:

          有一个简单的方法可以让 Linq 链式流畅,通过在 Linq 命名空间中设置 using 语句,然后可以直接调用 where 函数,无需调用静态 Where 函数。

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

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

          完美运行。

          【讨论】:

          • using namespace System.Linq 语句在这里既不需要也没有任何作用,因为您的命令根本没有使用 LINQ(PowerShell 不支持 .NET 扩展方法)。相反,您的命令正在调用 PowerShell 自己的 .Where() array method
          猜你喜欢
          • 2020-08-26
          • 1970-01-01
          • 2011-03-20
          • 1970-01-01
          • 2011-08-24
          • 2023-04-08
          • 1970-01-01
          • 1970-01-01
          • 2016-03-07
          相关资源
          最近更新 更多