如何在第一次匹配时停止管道过滤(Where-Object)

时间:2017-09-20 16:44:16

标签: powershell break pipeline

我搜索了但是还没找到怎么做。
我正在研究从大文件中过滤数据(~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

我想要的是是在第一场比赛后返回输出:

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 $_}}

但它仍然会返回完整的输出 参考:
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,但它不会返回该项目 这个可以通过以下方式解决:

Get-Process | Where-Object {if($_.ProcessName.StartsWith("svchost")){ $someVar = $_;break;}}  

因此我标记了他的答案。

3 个答案:

答案 0 :(得分:2)

Where-ObjectForEach-Object都是Cmdlet。你不能破坏Cmdlet(命令)。你可以做的是使用像这样的关键字foreach

$process = Get-Process

foreach ($item in $process) {
    if ($item.Name -eq 'svchost') {
        $item
        return
    }
}

答案 1 :(得分:2)

如果您需要效率,可以尝试将其分解为循环:

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],在任何数组或列表中,您可以使用方括号内的索引选择单个行,但要小心,因为在null或空对象上尝试此操作将会抛出异常:

(Get-Process | Where-Object {$_.ProcessName.StartsWith("svchost")})[0]

或者你可以Select-Object以类似的方式工作,但有更多选项而不仅仅是索引,如果对象为空或空,则不会抛出任何错误。

Get-Process | Where-Object {$_.ProcessName.StartsWith("svchost")} | Select-Object -First 1

在选择第一个结果之前,这两个选项仍然会评估整个列表。

答案 2 :(得分:0)

要过滤大文件中的数据,请使用$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() 代替常规PowerShell cmdlet:

(define-struct make-no-pairs[])
(define-struct some-pairs[p ps])