PowerShell 相当于 LINQ Any()? [英] PowerShell equivalent of LINQ Any()?

查看:31
本文介绍了PowerShell 相当于 LINQ Any()?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想从存储在 subversion 中的脚本位置找到所有顶级目录.

I would like to find all directories at the top level from the location of the script that are stored in subversion.

在 C# 中会是这样的

In C# it would be something like this

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

我在 PowerShell 中找到等效的Any()"有点困难,我不想经历调用扩展方法的尴尬.

I'm having a bit of difficulty finding the equivalent of "Any()" in PowerShell, and I don't want to go through the awkwardness of calling the extension method.

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

So far I've got this:

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

这为我找到了 svn 目录本身,而不是它们的父目录——这正是我想要的.如果你能告诉我为什么添加

This finds me the svn directories themselves, but not their parent directories - which is what I want. Bonus points if you can tell me why adding

 | Select-Object {$_.Directory}

在该命令列表的末尾仅显示一系列空行.

to the end of that command list simply displays a sequence of blank lines.

推荐答案

使用 PowerShell v3+ 解决方案回答即时问题:

To answer the immediate question with a PowerShell v3+ solution:

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

-Directory 限制对目录的匹配,-Recurse -Depth 2 最多递归三个级别(子、孙和曾孙),Include 允许指定多个(文件名组件)过滤器,并且 .Parent.FullName 返回 parent 目录的完整路径.使用成员枚举(隐式访问集合的元素属性).

-Directory limits the matches to directories, -Recurse -Depth 2 recurses up to three levels (children, grandchildren, and great-grandchildren), Include allows specifying multiple (filename-component) filters, and .Parent.FullName returns the full path of the parent dirs. of the matching dirs., using member enumeration (implicitly accessing a collection's elements' properties).

至于奖励问题:select-object {$_.Directory} 不起作用,因为 [Get-ChildItem 返回的 System.IO.DirectoryInfo] 实例没有 .Directory 属性,只有一个 .Parent属性;Select-Object -ExpandProperty Parent 应该已经使用了.

As for the bonus question: select-object {$_.Directory} does not work, because the [System.IO.DirectoryInfo] instances returned by Get-ChildItem have no .Directory property, only a .Parent property; Select-Object -ExpandProperty Parent should have been used.

除了只返回感兴趣的属性 value 之外,-ExpandProperty 还强制执行该属性的存在.相比之下,Select-Object {$_.Directory} 返回一个自定义对象,其属性字面名为 $_.Directory,其值为$null,假设输入对象没有 .Directory 属性;这些 $null 值在控制台中打印为空行.

In addition to only returning the property value of interest, -ExpandProperty also enforces the existence of the property. By contrast, Select-Object {$_.Directory} returns a custom object with a property literally named $_.Directory, whose value is $null, given that the input objects have no .Directory property; these $null values print as empty lines in the console.

至于关于 PowerShell 等价于 LINQ 的 .Any() 方法,表示[带有布尔结果]给定的可枚举(集合)是否有任何元素/任何满足给定条件的元素:

As for the more general question about a PowerShell equivalent to LINQ's .Any() method, which indicates [with a Boolean result] whether a given enumerable (collection) has any elements at all / any elements satisfying a given condition:

PowerShell 本身没有提供这样的等价物,但可以模拟的行为:

Natively, PowerShell offers no such equivalent, but the behavior can be emulated:

警告:这需要首先在内存中收集整个输入集合,这对于大型集合和/或长时间运行的输入命令可能会出现问题.

Caveat: This requires collecting the entire input collection in memory first, which can be problematic with large collections and/or long-running input commands.

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

... 表示感兴趣的命令,$_ ... 表示感兴趣的条件,应用于每个输入对象,其中PowerShell的自动$_ 变量指的是手头的输入对象;参数 'First' 确保该方法在找到第一个匹配项后返回.

... represents the command of interest, and $_ ... the condition of interest, applied to each input object, where PowerShell's automatic $_ variable refers to the input object at hand; argument 'First' ensures that the method returns once the first match has been found.

例如:

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

<小时>

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

基于管道的解决方案的优势是它可以对命令的输出一个接一个地进行操作,因为它正在生成,而无需首先在内存中收集整个输出.


Using the pipeline: Testing whether a command produced at least one output object [matching a condition]:

The advantage of a pipeline-based solution is that it can act on a command's output one by one, as it is being produced, without needing to collect the entire output in memory first.

  • 如果您不介意枚举所有对象 - 即使您只关心是否有至少一个- 使用 Paolo Tedesco 的有用扩展JaredPar 的有用答案.这种方法的缺点是您总是必须等待(可能长时间运行)命令完成生成所有 输出对象,即使 - 逻辑上 - 确定是否存在 任何输出对象都可以在收到第一个对象后立即生成.

  • If you don't mind that all objects are enumerated - even if you only care if there is at least one - use Paolo Tedesco's helpful extension to JaredPar's helpful answer. The down-side of this approach is that you always have to wait for a (potentially long-running) command to finish producing all output objects, even though - logically - the determination whether there are any output objects can be made as soon as the first object is received.

如果您想在遇到一个[匹配]对象后立即退出管道,您有两种选择:

If you want to exit the pipeline as soon as one [matching] object has been encountered, you have two options:

  • [Ad-hoc:易于理解,但实施起来很麻烦]将管道包含在 虚拟循环 中,并使用 break 中断管道 和该循环 (... 表示要测试其输出的命令,并且 $_ ... 与条件匹配):

  • [Ad-hoc: Easy to understand, but cumbersome to implement] Enclose the pipeline in a dummy loop and use break to break out of the pipeline and that loop (... represents the command whose output to test, and $_ ... match the condition):

 # 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 文件中.

  • [Use a PowerShell v3+ self-contained utility function that is nontrivial to implement] See the implementation of function Test-Any below. It can be added to scripts or, for use in interactive sessions, to your $PROFILE file.

    该功能很重要,因为从 Windows PowerShell v5.1、PowerShell Core v6 开始,没有直接方式提前退出管道,因此基于变通方法在 .NET 反射上,目前需要一个私有类型.

    The function is nontrivial, because as of Windows PowerShell v5.1, PowerShell Core v6, there is no direct way to exit a pipeline prematurely, so a workaround based on .NET reflection and a private type is currently necessary.

    如果您同意应该有这样的功能,请参与 在 GitHub 上的对话.

    If you agree that there should be such a feature, take part in the conversation on GitHub.

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

  • 查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆