代码之家  ›  专栏  ›  技术社区  ›  George Mauer

PowerShell等同于LINQ Any()?

  •  63
  • George Mauer  · 技术社区  · 16 年前

    在C#中,它应该是这样的

    Directory.GetDirectories(".")
      .Where(d=>Directories.GetDirectories(d)
         .Any(x => x == "_svn" || ".svn"));
    

     Get-ChildItem | ? {$_.PsIsContainer} | Get-ChildItem -force | ? {$_.PsIsContainer -and $_.Name -eq "_svn" -or $_.Name -eq ".svn"
    

    这让我找到了 svn

     | Select-Object {$_.Directory}
    

    在该命令列表的末尾,只显示一系列空白行。

    13 回复  |  直到 7 年前
        1
  •  40
  •   mklement0    6 年前

    回答 立即

    (Get-ChildItem -Force -Directory -Recurse -Depth 2 -Include '_svn', '.svn').Parent.FullName
    

    -Directory 将匹配限制到目录, -Recurse -Depth 2 Include 允许指定多个(文件名组件)过滤器,以及 .Parent.FullName member-access enumeration 元素' 属性)。

    select-object {$_.Directory} 不起作用, 因为 [System.IO.DirectoryInfo] 返回的实例 Get-ChildItem 没有 .Directory 财产,只有a .Parent Select-Object -ExpandProperty Parent

    除了只归还房产 感兴趣的, -ExpandProperty Select-Object {$_.Directory} 自定义对象 $_.Directory $null ,假设输入对象没有 .目录 财产;这些


    至于更多 通用 问题 大约一个 PowerShell相当于 LINQ's .Any() 方法 ,它[用布尔结果]指示给定的可枚举(集合)是否有任何元素/任何满足给定条件的元素:

    这种等价物 ,但行为可以 仿真 :


    intrinsic .Where() method :

    :这个 需要先在内存中收集整个输入集合 ,这对于大型集合和/或长时间运行的输入命令来说可能是有问题的。

    (...).Where({ $_ ... }, 'First').Count -gt 0
    

    ... 代表感兴趣的命令,以及 $_ ... 感兴趣的条件,应用于每个输入对象,其中PowerShell的自动 $_ 'First' 确保该方法在找到第一个匹配项后返回。

    例如:

    # See if there's at least one value > 1
    PS> (1, 2, 3).Where({ $_ -gt 1 }, 'First').Count -gt 0
    True
    

    使用 :测试命令是否产生至少一个输出对象[匹配条件]:

    一个接一个地生产 ,无需先将整个输出收集到内存中 .

    • 全部 对象被枚举 至少一个 -使用 Paolo Tedesco's helpful extension JaredPar's helpful answer . 全部 输出对象,即使在逻辑上确定是否存在 输出对象可以在 第一 对象被接收。

    • 如果你想 退出管道 一……就…… 遇到[匹配]对象

      • 即席:易于理解,但实施起来很麻烦 ] 将管道封闭在 虚拟回路 使用 break 冲出管道 那个循环( ... 表示要测试其输出的命令,以及 符合条件):

         # Exit on first input object.
         [bool] $haveAny = do { ... | % { $true; break } } while ($false)
        
         # Exit on first input object that matches a condition.
         [bool] $haveAny = do { ... | % { if ($_ ...) { $true ; break } } } while ($false)
        
      • [ 使用PowerShell v3+自包含的实用程序函数,实现起来并不简单 ] Test-Any 在......下面 . 它可以添加到脚本中,也可以添加到您的 $PROFILE 文件。


    效用函数 测试任意

    如果你同意应该有这样一个功能,请参加对话 GitHub issue #3821 .

    #requires -version 3
    Function Test-Any {
    
        [CmdletBinding()]
        param(
            [ScriptBlock] $Filter,
            [Parameter(ValueFromPipeline = $true)] $InputObject
        )
    
        process {
          if (-not $Filter -or (Foreach-Object $Filter -InputObject $InputObject)) {
              $true # Signal that at least 1 [matching] object was found
              # Now that we have our result, stop the upstream commands in the
              # pipeline so that they don't create more, no-longer-needed input.
              (Add-Type -Passthru -TypeDefinition '
                using System.Management.Automation;
                namespace net.same2u.PowerShell {
                  public static class CustomPipelineStopper {
                    public static void Stop(Cmdlet cmdlet) {
                      throw (System.Exception) System.Activator.CreateInstance(typeof(Cmdlet).Assembly.GetType("System.Management.Automation.StopUpstreamCommandsException"), cmdlet);
                    }
                  }
                }')::Stop($PSCmdlet)
          }
        }
        end { $false }
    }
    
    • if (-not $Filter -or (Foreach-Object $Filter -InputObject $InputObject)) 如果满足以下条件,则默认为true $Filter 未指定,否则将使用手头的对象计算筛选器(脚本块)。

    • (Add-Type ... 语句使用C#代码创建的ad-hoc类型,该类型使用反射抛出与 Select-Object -First [System.Management.Automation.StopUpstreamCommandsException] ,截至PowerShell v5,它仍然是 http://powershell.com/cs/blogs/tobias/archive/2010/01/01/cancelling-a-pipeline.aspx 非常感谢 PetSerAl

    示例:

    • PS> @() | Test-Any false

    • PS> Get-EventLog Application | Test-Any # should return *right away* true

    • PS> 1, 2, 3 | Test-Any { $_ -gt 1 } # see if any object is > 1 true


    JaredPar的有用答案 Paolo Tedesco's helpful extension

    遗憾的是,即使在PowerShell v5中,也没有直接的方法可以提前退出管道 . 如果你同意应该有这样一个功能,请参加对话 .

    优化 JaredPar's answer

    # IMPORTANT: ONLY EVER USE THIS INSIDE A PURPOSE-BUILT DUMMY LOOP (see below)
    function Test-Any() { process { $true; break } end { $false } }
    
    • process

      • 过程 着手 $null 测试任意 外面 管道回流无益 $true $null | Test-Any 测试任意 ,检查 $MyInvocation.ExpectingInput ,即 $true PetSerAl function Test-Any() { process { $MyInvocation.ExpectingInput; break } end { $false } }
    • $true

    • 打破 但是,它也退出任何封闭循环- 打破 PetSerAl .

      • 如果有
      • 请注意 return 只需继续
    • 自从 过程 block无条件执行 打破 ,the end 过程 $false 被写入输出流以发出信号。

        2
  •  23
  •   Timores    4 年前

    遗憾的是,PowerShell中没有相应的功能。我写了一篇关于这一点的博客文章,建议使用通用的Test-Any函数/过滤器。

    function Test-Any() {
        begin {
            $any = $false
        }
        process {
            $any = $true
        }
        end {
            $any
        }
    }
    

    Is there anything in that pipeline?

        3
  •  18
  •   Paolo Tedesco    11 年前

    Test-Any 筛选器:

    function Test-Any {
        [CmdletBinding()]
        param($EvaluateCondition,
            [Parameter(ValueFromPipeline = $true)] $ObjectToTest)
        begin {
            $any = $false
        }
        process {
            if (-not $any -and (& $EvaluateCondition $ObjectToTest)) {
                $any = $true
            }
        }
        end {
            $any
        }
    }
    

    > 1..4 | Test-Any { $_ -gt 3 }
    True
    
    > 1..4 | Test-Any { $_ -gt 5 }
    False
    
        4
  •  10
  •   Peter Mortensen Pieter Jan Bonestroo    7 年前

    您可以使用原始的LINQ Any :

    [Linq.Enumerable]::Any($list)
    
        5
  •  5
  •   Joey Gumbo    16 年前

    [bool] ($source `
            | foreach { [bool] (<predicate>) } `
            | where { $_ } `
            | select -first 1)
    

    替代方式:

    ($source `
            | where { <predicate> } `
            | foreach { $true } `
            | select -first 1)
    
        6
  •  5
  •   Michael Logutov    7 年前

    gci -r -force `
        | ? { $_.PSIsContainer -and $_.Name -match "^[._]svn$" } `
        | select Parent -Unique
    

    select-object {$_.Directory}
    

    没有返回任何有用的东西,因为在一个 DirectoryInfo 对象。至少不是在我的PowerShell中。


    详细说明你自己的答案:PowerShell可以将大多数非空集合视为 $true

    $svnDirs = gci `
        | ? {$_.PsIsContainer} `
        | ? {
            gci $_.Name -Force `
                | ? {$_.PSIsContainer -and ($_.Name -eq "_svn" -or $_.Name -eq ".svn") }
            }
    
        7
  •  3
  •   Peter Mortensen Pieter Jan Bonestroo    7 年前

    我最后用一个数字来计算:

    $directoryContainsSvn = {
        (Get-ChildItem $_.Name -force | ? {$_.PsIsContainer -and $_.Name -eq "_svn" -or $_.Name -eq ".svn"} | Measure-Object).Count -eq 1
    }
    $svnDirs = Get-ChildItem | ? {$_.PsIsContainer} | ? $directoryContainsSvn
    
        8
  •  2
  •   Keith Hill    16 年前

    你可以稍微收紧一下:

    gci -fo | ?{$_.PSIsContainer -and `
                (gci $_ -r -fo | ?{$_.PSIsContainer -and $_ -match '[_.]svn$'})}
    

        9
  •  1
  •   MovGP0    11 年前

    我推荐以下解决方案:

    <#
    .SYNOPSIS 
       Tests if any object in an array matches the expression
    
    .EXAMPLE
        @( "red", "blue" ) | Where-Any { $_ -eq "blue" } | Write-Host
    #>
    function Where-Any 
    {
        [CmdletBinding()]
        param(
            [Parameter(Mandatory = $True)]
            $Condition,
    
            [Parameter(Mandatory = $True, ValueFromPipeline = $True)]
            $Item
        )
    
        begin {
            [bool]$isMatch = $False
        }
    
        process {
          if (& $Condition $Item) {
              [bool]$isMatch = $true
          }
        }
    
        end {
            Write-Output $isMatch
        }
    }
    
    # optional alias
    New-Alias any Where-Any
    
        10
  •  1
  •   Peter Mortensen Pieter Jan Bonestroo    7 年前

    LINQ Any() equivalent in PowerShell

    因此,所需的代码可能如下:

    function Test-Any([scriptBlock] $scriptBlock = {$true}, [scriptBlock] $debugOut = $null)
    {
        if ($debugOut)
        {
            Write-Host(“{0} | % {{{1}}}” -f $input, $scriptBlock)
        }
    
        $_ret = $false;
        $_input = ($input -as [Collections.IEnumerator])
    
        if ($_input)
        {
            while ($_input.MoveNext())
            {
                $_ = $_input.Current;
    
                Write-Host $_
    
                if ($debugOut)
                {
                    Write-Host(“Tested: [{0}]” -f (&$debugOut))
                }
    
                if (&$scriptBlock)
                {
                    if ($debugOut)
                    {
                        Write-Host(“Matched: [{0}]” -f (&$debugOut))
                    }
    
                    $_ret = $true
                    break
                }
            }
        }
    
        $_ret
    }
    
        11
  •  1
  •   Peter Mortensen Pieter Jan Bonestroo    7 年前

    Any 俏皮话 :

    # Any item is greater than 5
    $result = $arr | %{ $match = $false }{ $match = $match -or $_ -gt 5 }{ $match }
    

    %{ $match = $false }{ $match = $match -or YOUR_CONDITION }{ $match } 检查是否至少有一个项目符合条件。

    任何 操作对数组进行求值,直到找到符合条件的第一个项。但是 .

    只需提一下,你可以很容易地调整它,使其成为 All 俏皮话 :

    # All items are greater than zero
    $result = $arr | %{ $match = $false }{ $match = $match -and $_ -gt 0 }{ $match }
    

    %{ $match = $false }{ $match = $match -and YOUR_CONDITION }{ $match } 检查所有项目是否符合条件。

    注意,要检查任何你需要的 -or 并检查你需要的一切 -and .

        12
  •  0
  •   rdelgado-incinc    4 年前

    我采取了更林克式的方法。

    PS> $searchData = "unn"
    PS> $StringData = @("unn", "dew", "tri", "peswar", "pymp")
    PS> $delegate =  [Func[string,bool]]{ param($d); return $d -eq $searchData }
    PS> [Linq.Enumerable]::Any([string[]]$StringData, $delegate)
    

    从这里拍摄:

    https://www.red-gate.com/simple-talk/dotnet/net-framework/high-performance-powershell-linq/#post-71022-_Toc482783751