可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
How can I use the Array.Find method in powershell?
For example:
$a = 1,2,3,4,5
[Array]::Find($a, { args[0] -eq 3 })
gives
Cannot find an overload for "Find" and the argument count: "2".
At line:3 char:1
+ [Array]::Find($a, { $args[0] -eq 3 })
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodException
+ FullyQualifiedErrorId : MethodCountCouldNotFindBest
The array class has the methods I expect, as given by:
PS> [Array] | Get-Member -Static
TypeName: System.Array
Name MemberType Definition
---- ---------- ----------
Find Method static T Find[T](T[] array, System.Predicate[T] match)
Should the array be casted to something else to match the T[] type? I know there are other ways to achieve the find functionality, but I was wondering why this doesn't work.
回答1:
You need to cast the ScriptBlock
as a Predicate[T]
. Consider the following example:
[Array]::Find(@(1,2,3), [Predicate[int]]{ $args[0] -eq 1 })
# Result: 1
The reason that you received the error, is because there was no matching method overload, in the case where you're passing in a PowerShell ScriptBlock
. As you noted in your Get-Member
output, there is no Find()
method overload that accepts a ScriptBlock
as its second parameter.
[Array]::Find(@(1,2,3), { $args[0] -eq 1 })
Cannot find an overload for "Find" and the argument count: "2".
At line:1 char:17
+ [Array]::Find(@(1,2,3), { $_ -eq 1 })
+ ~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodException
+ FullyQualifiedErrorId : MethodCountCouldNotFindBest
回答2:
There is no need to use Array.Find, a regular where
clause would work fine:
$a = @(1,2,3,4,5)
$a | where { $_ -eq 3 }
Or this (as suggested by @mjolinor):
$a -eq 3
Or this (returns $true
or $false
):
$a -contains 3
Where
clause supports any type of objects, not just basic types, like this:
$a | where { $_.SomeProperty -eq 3 }
回答3:
Another option would be using an ArrayList
, which provides a Contains
method:
PS C:\> [Collections.ArrayList]$a = 'a', 'b', 'c'
PS C:\> $a.Contains('b')
True
PS C:\> $a.Contains('d')
False
Or, as @Neolisk mentioned in the comments, you could use PowerShell's -contains
operator:
PS C:\> $a = 'a', 'b', 'c'
PS C:\> $a -contains 'b'
True
PS C:\> $a -contains 'd'
False
回答4:
Trevor Sullivan's answer is the right one, not only for Find() static method, but for FindIndex() as well.
When you've got several NIC cards with both ipv4 & ipv6 active on your servers and want to check the ipv4 IP/netmask pairs, something like this is good :
$NetworkAdapters = Get-WmiObject Win32_NetworkAdapterConfiguration -Filter 'IPEnabled = True' | Select-Object -Property Description, IPAddress, IPSubnet, DefaultIPGateway, DNSServerSearchOrder, DNSDomain
$NetworkAdapters | % {
"Adapter {0} :" -f $_.Description
# array'ing to avoid failure against single homed netcards
$idx = [System.Array]::FindIndex(@($_.IPAddress), [Predicate[string]]{ $args[0] -match "\d+.\d+.\d+.\d+" })
" IP {0} has netmask {1}" -f @($_.IPAddress[$idx]), @($_.IPSubnet)[$idx]
}
My point is it works like a charm on 2012 WinPE, and fails on a production Win7 wks. Anyone got an idea ?
回答5:
This was run across ~6 million items in a system.array using both methods
$s=get-date
$([array]::FindALL($OPTArray,[Predicate[string]]{ $args[0] -match '^004400702(_\d{5})?' })).count
$(New-TimeSpan -Start $s -End $(get-date)).TotalSeconds
20 items
33.2223219 seconds
$s=get-date
$($OPTArray | where { $_ -match '^004400702(_\d{5})?'}).count
$(New-TimeSpan -Start $s -End $(get-date)).TotalSeconds
20 items
102.1832173 seconds