如何在第一次匹配时停止管道过滤(Where-Object)
How to stop pipeline filtering (Where-Object) on first match
我搜索了,但还没有找到方法。
我正在从大文件 (~2GB) 中过滤数据。
我使用 Where-Object
并且当它找到匹配项时,它会继续搜索它有意义的其他匹配项。
是否可以在第一场比赛就停止?
例如(#1):
Get-Process | Where-Object {$_.ProcessName.StartsWith("svchost")}
输出将是:
Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName
------- ------ ----- ----- ----- ------ -- -----------
666 38 26928 18672 92 568 svchost
596 28 11516 16560 92 792 svchost
425 14 5364 7036 45 832 svchost
406 17 7032 8416 39 1004 svchost
我要的是return第一次匹配后的输出:
Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName
------- ------ ----- ----- ----- ------ -- -----------
666 38 26928 18672 92 568 svchost
这是我尝试过的(也使用 Foreach-Object):
Get-Process | Where-Object {if($_.ProcessName.StartsWith("svchost")){return $_}}
Get-Process | Where-Object {if($_.ProcessName.StartsWith("svchost")){return $_;break;}}
Get-Process | ForEach-Object {if($_.ProcessName.StartsWith("svchost")){return $_}}
但它仍然是 return 的完整输出。
参考:
How to break Foreach loop in Powershell?
Is it possible to terminate or stop a PowerShell pipeline from within a filter
编辑(大数据问题的解释):
示例 (#2):
我有两个 XML:
A.xml:
<?xml version="1.0" encoding="UTF-8"?>
<Events>
<Event>
<EventData Name="Time">09/10/2017 12:54:16</EventData>
<EventData Name="WorkstationName">USER2-PC</EventData>
<EventData Name="UserName">user2</EventData>
</Event>
</Events>
B.xml:
<?xml version="1.0" encoding="UTF-8"?>
<Events>
<Event>
<EventData Name="Time">09/10/2017 14:54:16</EventData>
<EventData Name="WorkstationName">USER1-PC</EventData>
<EventData Name="UserName">user1</EventData>
</Event>
<Event>
<EventData Name="Time">09/10/2017 13:54:16</EventData>
<EventData Name="WorkstationName">USER2-PC</EventData>
<EventData Name="UserName">user2</EventData>
</Event>
... (more 100,000 events like the above two)
</Events>
这些 XML 正在作为对象加载:
$fileA = "C:\tmp\A.xml"
$a = New-Object Xml.XmlDocument
$a.Load($fileA)
$fileB = "C:\tmp\B.xml"
$b = New-Object Xml.XmlDocument
$b.Load($fileB)
然后我想搜索相同用户名的第一个匹配项:
$result = $b.Events.Event | Where-Object {
(($_.EventData | where-object {$_.Name -eq "UserName"})."#text" -eq $username)
}
$result.EventData
在这种情况下,如果我在第一个事件中匹配,那么在其余 99,999 个事件中 运行 会浪费时间。
编辑(已解决):
看完尼克的回答后,没有什么新东西我没试过。
命令:
Get-Process | Where-Object {if($_.ProcessName.StartsWith("svchost")){ $_;break;}}
确实停止了 Where-Object
但它没有 return 项目。
这个可以通过以下方式解决:
Get-Process | Where-Object {if($_.ProcessName.StartsWith("svchost")){ $someVar = $_;break;}}
所以我标记了他的答案。
Where-Object
和 ForEach-Object
都是 Cmdlet。您不能破坏 Cmdlet(命令)。您可以做的是像这样使用关键字 foreach
$process = Get-Process
foreach ($item in $process) {
if ($item.Name -eq 'svchost') {
$item
return
}
}
要从大文件中过滤数据,请使用 StreamReader
而不是常规的 PowerShell cmdlet:
$filename = 'C:\path\to\your.txt'
$word = 'something'
$rdr = [IO.File]::OpenText($filename)
while ($rdr.Peek() -ge 0) {
$line = $rdr.ReadLine()
if ($line -like "*${word}*") { break }
}
$rdr.Close()
$rdr.Dispose()
如果 efficiency 是您所需要的,您可以尝试将其分解为一个循环:
Get-Process | foreach {If ($_.ProcessName.StartsWith("svchost")){$_;break}}
您可以确认它适用于此检查:
$i=0; Get-Process | foreach {$i++;$i; If ($_.ProcessName.StartsWith("svchost")){$_;break}}
它会让循环在每次循环时打印出一个数字,在我的例子中它达到了 115,然后如果我这样做 (Get-Process).Count
我有 157
个进程,所以它循环了我的进程找到我们想要的然后停止循环。
如其他答案中所述,您可以使用 [0]
,在任何数组或列表上,您可以使用方括号内的索引 select 单独的行,但要小心,因为尝试这样做null 或空对象将引发异常:
(Get-Process | Where-Object {$_.ProcessName.StartsWith("svchost")})[0]
或者您可以 Select-Object
,它以类似的方式工作,但除了 Index 之外还有更多选项,并且如果对象为 null 或空,则不会抛出任何错误。
Get-Process | Where-Object {$_.ProcessName.StartsWith("svchost")} | Select-Object -First 1
这两个选项如何仍然会在您 select 第一个结果之前评估整个列表。
超级有趣。不知道为什么,这篇文章与我们的发现相矛盾!
我什至测试过它。由于 PS3,select-object -first
停止管道
我搜索了,但还没有找到方法。
我正在从大文件 (~2GB) 中过滤数据。
我使用 Where-Object
并且当它找到匹配项时,它会继续搜索它有意义的其他匹配项。
是否可以在第一场比赛就停止?
例如(#1):
Get-Process | Where-Object {$_.ProcessName.StartsWith("svchost")}
输出将是:
Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName
------- ------ ----- ----- ----- ------ -- -----------
666 38 26928 18672 92 568 svchost
596 28 11516 16560 92 792 svchost
425 14 5364 7036 45 832 svchost
406 17 7032 8416 39 1004 svchost
我要的是return第一次匹配后的输出:
Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName
------- ------ ----- ----- ----- ------ -- -----------
666 38 26928 18672 92 568 svchost
这是我尝试过的(也使用 Foreach-Object):
Get-Process | Where-Object {if($_.ProcessName.StartsWith("svchost")){return $_}}
Get-Process | Where-Object {if($_.ProcessName.StartsWith("svchost")){return $_;break;}}
Get-Process | ForEach-Object {if($_.ProcessName.StartsWith("svchost")){return $_}}
但它仍然是 return 的完整输出。
参考:
How to break Foreach loop in Powershell?
Is it possible to terminate or stop a PowerShell pipeline from within a filter
编辑(大数据问题的解释):
示例 (#2):
我有两个 XML:
A.xml:
<?xml version="1.0" encoding="UTF-8"?>
<Events>
<Event>
<EventData Name="Time">09/10/2017 12:54:16</EventData>
<EventData Name="WorkstationName">USER2-PC</EventData>
<EventData Name="UserName">user2</EventData>
</Event>
</Events>
B.xml:
<?xml version="1.0" encoding="UTF-8"?>
<Events>
<Event>
<EventData Name="Time">09/10/2017 14:54:16</EventData>
<EventData Name="WorkstationName">USER1-PC</EventData>
<EventData Name="UserName">user1</EventData>
</Event>
<Event>
<EventData Name="Time">09/10/2017 13:54:16</EventData>
<EventData Name="WorkstationName">USER2-PC</EventData>
<EventData Name="UserName">user2</EventData>
</Event>
... (more 100,000 events like the above two)
</Events>
这些 XML 正在作为对象加载:
$fileA = "C:\tmp\A.xml"
$a = New-Object Xml.XmlDocument
$a.Load($fileA)
$fileB = "C:\tmp\B.xml"
$b = New-Object Xml.XmlDocument
$b.Load($fileB)
然后我想搜索相同用户名的第一个匹配项:
$result = $b.Events.Event | Where-Object {
(($_.EventData | where-object {$_.Name -eq "UserName"})."#text" -eq $username)
}
$result.EventData
在这种情况下,如果我在第一个事件中匹配,那么在其余 99,999 个事件中 运行 会浪费时间。
编辑(已解决):
看完尼克的回答后,没有什么新东西我没试过。
命令:
Get-Process | Where-Object {if($_.ProcessName.StartsWith("svchost")){ $_;break;}}
确实停止了 Where-Object
但它没有 return 项目。
这个可以通过以下方式解决:
Get-Process | Where-Object {if($_.ProcessName.StartsWith("svchost")){ $someVar = $_;break;}}
所以我标记了他的答案。
Where-Object
和 ForEach-Object
都是 Cmdlet。您不能破坏 Cmdlet(命令)。您可以做的是像这样使用关键字 foreach
$process = Get-Process
foreach ($item in $process) {
if ($item.Name -eq 'svchost') {
$item
return
}
}
要从大文件中过滤数据,请使用 StreamReader
而不是常规的 PowerShell cmdlet:
$filename = 'C:\path\to\your.txt'
$word = 'something'
$rdr = [IO.File]::OpenText($filename)
while ($rdr.Peek() -ge 0) {
$line = $rdr.ReadLine()
if ($line -like "*${word}*") { break }
}
$rdr.Close()
$rdr.Dispose()
如果 efficiency 是您所需要的,您可以尝试将其分解为一个循环:
Get-Process | foreach {If ($_.ProcessName.StartsWith("svchost")){$_;break}}
您可以确认它适用于此检查:
$i=0; Get-Process | foreach {$i++;$i; If ($_.ProcessName.StartsWith("svchost")){$_;break}}
它会让循环在每次循环时打印出一个数字,在我的例子中它达到了 115,然后如果我这样做 (Get-Process).Count
我有 157
个进程,所以它循环了我的进程找到我们想要的然后停止循环。
如其他答案中所述,您可以使用 [0]
,在任何数组或列表上,您可以使用方括号内的索引 select 单独的行,但要小心,因为尝试这样做null 或空对象将引发异常:
(Get-Process | Where-Object {$_.ProcessName.StartsWith("svchost")})[0]
或者您可以 Select-Object
,它以类似的方式工作,但除了 Index 之外还有更多选项,并且如果对象为 null 或空,则不会抛出任何错误。
Get-Process | Where-Object {$_.ProcessName.StartsWith("svchost")} | Select-Object -First 1
这两个选项如何仍然会在您 select 第一个结果之前评估整个列表。
超级有趣。不知道为什么,这篇文章与我们的发现相矛盾!
我什至测试过它。由于 PS3,select-object -first
停止管道