PowerShell相当于LINQ Any()?

时间:2009-09-30 17:07:23

标签: linq powershell

我想从subversion中存储的脚本位置找到顶层的所有目录。

在C#中它会是这样的

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

我在PowerShell中找到相当于“Any()”的方法有点困难,而且我不想经历调用扩展方法的尴尬。

到目前为止,我已经得到了这个:

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

这找到了svn目录本身,但不是它们的父目录 - 这就是我想要的。如果你能告诉我添加

的原因,可以获得奖励积分
 | Select-Object {$_.Directory}

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

11 个答案:

答案 0 :(得分:22)

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

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

博文: Is there anything in that pipeline?

答案 1 :(得分:16)

@ JaredPar答案的变体,将测试纳入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

答案 2 :(得分:8)

您可以使用原始LINQ Any

[Linq.Enumerable]::Any($list)

答案 3 :(得分:5)

我现在的做法是:

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

答案 4 :(得分:4)

实际上非常简单 - 只需先选择$ true(为清晰起见而格式化):

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

替代方式:

($source `
        | where { <predicate> } `
        | foreach { $true } `
        | select -first 1)

答案 5 :(得分:3)

我最终用计数来做这件事:

$directoryContainsSvn = {
    (Get-ChildItem $_.Name -force | ? {$_.PsIsContainer -and $_.Name -eq "_svn" -or $_.Name -eq ".svn"} | Measure-Object).Count -eq 1
}
$svnDirs = Get-ChildItem | ? {$_.PsIsContainer} | ? $directoryContainsSvn

答案 6 :(得分:2)

你可以稍微收紧一下:

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

注意 - 将$ __。名称传递给嵌套的gci是不必要的。通过它$ _就足够了。

答案 7 :(得分:1)

我推荐以下解决方案:

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

答案 8 :(得分:1)

这是我到目前为止找到的最好的方法(如果已经找到了true,则不会迭代所有元素,并且不会破坏管道):

来自 LINQ Any() equivalent in PowerShell

可以在函数范围内使用包含整个管道的内置$ input变量。

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

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
}

答案 9 :(得分:1)

我认为这里最好的答案是@JaredPar提出的功能,但如果你像我一样喜欢单行,我想提议 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 }检查至少一个项目匹配条件。

一个注意事项 - 通常 Any 操作会对数组进行求值,直到找到匹配条件的第一个项目为止。但此代码会评估所有项目

简而言之,您可以轻松将其调整为 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

答案 10 :(得分:0)

我采用了更 linq 风格的方法。

我知道这个问题可能很老了。我用它来满足我的需求:

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