PowerShell相当于LINQ Any()? [英] PowerShell equivalent of 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#中会是这样
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 目录的完整路径.使用成员枚举(隐式访问集合的 elements'属性)来匹配匹配的目录.
-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}
不起作用,
因为 \[System.IO.DirectoryInfo\]
Get-ChildItem
返回的a>实例没有.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
还强制执行了该属性的存在.相比之下,假定输入对象没有.Directory
属性,Select-Object {$_.Directory}
返回一个自定义对象,该对象的文字名称为$_.Directory
,其值为$null
.这些$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.
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提供了 no 此类等效功能,但是可以模拟这种行为:
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的有用扩展来
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.
如果您想在遇到一个 [matching]对象后立即退出管道 ,则有两个选择:
If you want to exit the pipeline as soon as one [matching] object has been encountered, you have two options:
-
[即席:易于理解,但实施起来很麻烦] 用 dummy循环封闭管道,并使用
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开始,没有 direct 方法过早退出管道,因此基于变通办法在.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上的对话 a>.
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 }
}
-
如果未指定
-
if (-not $Filter -or (Foreach-Object $Filter -InputObject $InputObject))
的默认值为true,否则将使用手边的对象评估过滤器(脚本块).if (-not $Filter -or (Foreach-Object $Filter -InputObject $InputObject))
defaults to true if$Filter
wasn't specified, and otherwise evaluates the filter (script block) with the object at hand.- 使用
ForEach-Object
评估过滤器脚本块可确保$_
在 all 方案中绑定到当前管道对象,如此处.
- The use of
ForEach-Object
to evaluate the filter script block ensures that$_
binds to the current pipeline object in all scenarios, as demonstrated in PetSerAl's helpful answer here.
(Add-Type ...
语句使用通过C#代码创建的即席类型,该类型使用反射引发与Select-Object -First
(PowerShell v3 +)在内部用于停止管道的相同异常,即[System.Management.Automation.StopUpstreamCommandsException]
,该异常从PowerShell v5开始,仍然是 private 类型. 这里的背景: http://powershell.com/cs /blogs/tobias/archive/2010/01/01/cancelling-a-pipeline.aspx 非常感谢 PetSerAl 在评论中提供了此代码.The
(Add-Type ...
statement uses an ad-hoc type created with C# code that uses reflection to throw the same exception thatSelect-Object -First
(PowerShell v3+) uses internally to stop the pipeline, namely[System.Management.Automation.StopUpstreamCommandsException]
, which as of PowerShell v5 is still a private type. Background here: http://powershell.com/cs/blogs/tobias/archive/2010/01/01/cancelling-a-pipeline.aspx A big thank-you to PetSerAl for contributing this code in the comments.示例:
-
PS> @() | Test-Any false
PS> @() | Test-Any false
PS> Get-EventLog Application | Test-Any # should return *right away* true
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
PS> 1, 2, 3 | Test-Any { $_ -gt 1 } # see if any object is > 1 true
JaredPar的有用答案和Paolo Tedesco的有用扩展功能在一个方面是不足的:一旦有比赛,他们就不会退出管道被发现,这可能是一个重要的优化.
JaredPar's helpful answer and Paolo Tedesco's helpful extension fall short in one respect: they don't exit the pipeline once a match has been found, which can be an important optimization.
遗憾的是,即使从PowerShell v5开始,也没有直接方法过早退出管道. 如果您同意应该有这样的功能,请参加GitHub上的对话 .
Sadly, even as of PowerShell v5, there is no direct way to exit a pipeline prematurely. If you agree that there should be such a feature, take part in the conversation on GitHub.
JaredPar的答案的天真优化实际上缩短了代码:
A naïve optimization of JaredPar's answer actually shortens the code:
# 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
):谢谢,- The
process
block is only entered if there's at least one element in the pipeline.- Small caveat: By design, if there's no pipeline at all, the
process
block is still entered, with$_
set to$null
, so callingTest-Any
outside of a pipeline unhelpfully returns$true
. To distinguish between between$null | Test-Any
andTest-Any
, check$MyInvocation.ExpectingInput
, which is$true
only in a pipeline: Thanks, PetSerAlfunction Test-Any() { process { $MyInvocation.ExpectingInput; break } end { $false } }
- 如果有 命令退出管道,这就是它的去向.
- 请注意,
return
会简单地移至 next 输入对象. - If there were a command to exit the pipeline, this is where it would go.
- Note that
return
would simply move on to the next input object.
break
终止管道,因此可以避免多余处理其他对象. 但是,它也退出了任何封闭的循环-break
并非旨在退出管道 感谢, PetSerAlbreak
then terminates the pipeline and thus prevents superfluous processing of additional objects. HOWEVER, IT ALSO EXITS ANY ENCLOSING LOOP -break
is NOT designed to exit a PIPELINEThanks, PetSerAl .由于
process
块无条件执行break
,因此只有在从未输入过process
块的情况下才到达end
块,这意味着流水线为空,因此将$false
写入到输出流来发出信号.Since the
process
block unconditionally executesbreak
, theend
block is only reached if theprocess
block was never entered, which implies an empty pipeline, so$false
is written to the output stream to signal that.这篇关于PowerShell相当于LINQ Any()?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!
- Small caveat: By design, if there's no pipeline at all, the
- The
- 小警告:根据设计,如果完全没有管道,则仍会输入
- 使用
$Filter
,则