我搜索了但是还没找到怎么做。
我正在研究从大文件中过滤数据(~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;}}
因此我标记了他的答案。
答案 0 :(得分:2)
Where-Object
和ForEach-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])