使用 PowerShell v3+ 解决方案回答直接问题:
(Get-ChildItem -Force -Directory -Recurse -Depth 2 -Include '_svn', '.svn').Parent.FullName
-Directory 将匹配限制为目录,-Recurse -Depth 2 递归最多三个级别(子、孙和曾孙),Include 允许指定多个(文件名-组件)过滤器,.Parent.FullName 返回parent 目录的完整路径。匹配的目录,使用member-access enumeration(隐式访问集合的elements'属性)。
至于红利问题:select-object {$_.Directory}不行,
因为Get-ChildItem返回的\[System.IO.DirectoryInfo\]实例没有.Directory属性,只有.Parent属性; Select-Object -ExpandProperty Parent 应该已经被使用了。
除了只返回感兴趣的属性value,-ExpandProperty 还强制属性的存在。相比之下,Select-Object {$_.Directory} 返回一个自定义对象,其属性字面名称为$_.Directory,其值为$null,假设输入对象没有.Directory 属性;这些$null 值在控制台中打印为空行。
至于关于 PowerShell 等效于 LINQ's .Any() 方法的更多一般问题,它表明 [with a Boolean result] 是否给定的可枚举(集合)有任何元素/任何满足给定条件的元素:
PowerShell 本身没有提供这样的等价物,但行为可以模拟:
使用 PowerShell v4+ .Where() 收集方法:
警告:这需要首先将整个输入集合收集到内存中,这对于大型集合和/或长时间运行的输入命令可能会出现问题。
(...).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
使用管道:测试一个命令是否产生了至少一个输出对象[匹配一个条件]:
基于管道的解决方案的优点在于它可以在命令的输出在生成时逐个处理,而无需首先将整个输出收集到内存中。
PowerShell v3+:优化实用功能Test-Any
该功能非常重要,因为从 Windows PowerShell v5.1、PowerShell Core v6 开始,没有直接方法可以提前退出管道,因此基于解决方法.NET 反射和私有类型目前是必要的。
如果您同意应该有这样的功能,请参与对话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 }
}
示例:
-
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's helpful answer 和 Paolo Tedesco's helpful extension 在一个方面存在不足:一旦找到匹配项,它们就不会退出管道,这可能是一项重要的优化。
遗憾的是,即使从 PowerShell v5 开始,也没有直接的方法可以提前退出管道。
如果您同意应该有这样的功能,请参与对话on GitHub。
JaredPar's answer 的 naïve 优化实际上缩短了代码:
# IMPORTANT: ONLY EVER USE THIS INSIDE A PURPOSE-BUILT DUMMY LOOP (see below)
function Test-Any() { process { $true; break } end { $false } }
-
process 块只有在管道中至少有一个元素时才会进入。
- 小警告:按照设计,如果根本没有管道,
process 块仍会进入,$_ 设置为 $null,因此调用 Test-Any 在管道之外 无济于事地返回$true。要区分$null | Test-Any 和Test-Any,请检查$MyInvocation.ExpectingInput,这是$true 仅在管道中:谢谢,PetSerAlfunction Test-Any() { process { $MyInvocation.ExpectingInput; break } end { $false } }
-
$true,写入输出流,表示至少找到一个对象。
-
break 然后终止管道,从而防止对其他对象进行多余的处理。 但是,它也会退出任何封闭循环 - break 并非旨在退出管道谢谢,PetSerAl
.
- 如果有 退出管道的命令,这就是它应该去的地方。
- 请注意,
return 只会移动到 next 输入对象。
-
由于process 块无条件执行break,因此只有在从未进入process 块的情况下才会到达end 块,这意味着一个空管道,因此$false 被写入输出流以发出信号。