我有一个脚本,可以在大量文件中查找正则表达式,例如地址或电话号码。我目前的脚本作为一项工作运行并工作,但速度非常慢。
目前我的开始工作方法按预期进行,一切都很缓慢。我正在寻找加快速度并更快返回结果的方法。如果可以的话
在浏览了各种帮助后,我冒险进入了 powershell 中的 Runspaces 世界。下面是我整理的代码,并简要了解了 Runspaces 的使用。
我的问题是如何使用运行空间,以便并行运行的 Get-Childitem 请求不会跨多个运行空间扫描同一文件。如果这可能的话?
我创建了 20,000 个包含垃圾的文件,并手动编辑了 2 个带有“番茄酱!”一词的文件里面。
10k 个文件是 .xml 10k 个文件是 .txt
我尝试不使用 PS v7 -parralel 参数,因为我想将我的脚本/GUI 交给其他非 IT 人员且不会安装高于 ISE 的员工
$Finished.text = 'Working.....'
#Get list of files to search through
$path = "C:\intel\spam"
Push-Location $path
$FILES = Get-ChildItem -filter *.XML -File
### 5 Runspace limit
$RunspacePool = [RunspaceFactory]::CreateRunspacePool(1,5)
$RunspacePool.ApartmentState = "MTA"
$RunspacePool.Open()
$runspaces = @()
# Setup scriptblock
$scriptblock = {
Param (
[object]$files
)
foreach($file in $files){
$test = select-string -Path $file -Pattern 'KETCHUP!' -List | select-object FileName,Path
if($test)
{
add-content -Path 'C:\intel\matches.txt' -Value $test.Filename
}
}
}
Write-Output "Starting search..."
$runspace = [PowerShell]::Create()
[void]$runspace.AddScript($scriptblock)
[void]$runspace.AddArgument($FILES) # <-- Send files to be searched
$runspace.RunspacePool = $RunspacePool
$AsyncObject = $runspace.BeginInvoke()
# Wait for runspaces to complete
while ($runspaces.Status.IsCompleted -notcontains $true) {}
# Cleanup runspaces
foreach ($runspace in $runspaces ) {
$runspace.Pipe.EndInvoke($runspace.Status)
$runspace.Pipe.Dispose()
}
# Cleanup runspace pool
$RunspacePool.Close()
$RunspacePool.Dispose()
$Data = $runspace.EndInvoke($AsyncObject)
Pop-Location
我的问题是如何使用运行空间,以便 并行运行的 Get-Childitem 请求将不会扫描 跨多个运行空间的同一文件。如果这可能的话?
这确实归结为逻辑,那就是分解文件以分块搜索,这是完全可行的。它的工作方式更像是这样。假设您有 8 文件和假设的 2-核心 CPU:
[File1] [File2] [File3] [File4] [File5] [File6] [File7] [File8] <- All files from Get-ChildItem
确定chunk_size(在这个假设场景中为4,因为8文件除以2核心为4)后,代码会将这些文件分成块:
Chunk 1: [File1] [File2] [File3] [File4]
Chunk 2: [File5] [File6] [File7] [File8]
该除法将存储在
$file_chunks
ArrayList:
$file_chunks:
Index 0: [File1] [File2] [File3] [File4]
Index 1: [File5] [File6] [File7] [File8]
现在,当并行处理开始时,每个 CPU 核心(或运行空间)都会拾取一个块:
CPU Core 1 (Runspace 1): Processing [File1] [File2] [File3] [File4]
CPU Core 2 (Runspace 2): Processing [File5] [File6] [File7] [File8]
每个核心都处理自己的文件子集,从而实现更快的并行处理。
完成此操作后,您可以创建更强大的解决方案,例如以更频繁的方式重用它的函数:)
function Search-Files {
Param(
[Parameter()]
[string]$Path,
[Parameter()]
[string]$Filter,
[Parameter()]
[string]$Pattern
)
$runspace_pool = [runspacefactory]::CreateRunspacePool(1, [Environment]::ProcessorCount)
$runspace_pool.Open()
$runspaces = [System.Collections.ArrayList]::new()
$files = Get-ChildItem -Path $path -Filter $filter -File
$file_chunks = [System.Collections.ArrayList]::new()
$chunk_size = [Math]::Ceiling($files.Count / [Environment]::ProcessorCount)
0..([Environment]::ProcessorCount - 1) | ForEach-Object {
$start = $_ * $chunk_size
$end = $start + $chunk_size - 1
$null = $file_chunks.Add($files[$start..$end])
}
$scriptblock = {
Param($files, $pattern)
$results = [System.Collections.ArrayList]::new()
foreach ($file in $files)
{
try
{
$reader = [System.IO.File]::OpenText($file.FullName)
while ($reader.Peek() -ge 0)
{
$line = $reader.ReadLine()
if ($line -match $pattern)
{
$null = $results.Add($file.FullName)
break
}
}
}
finally
{
if ($reader)
{
$reader.Dispose()
}
}
}
return $results
}
foreach ($chunk in $file_chunks)
{
$runspace = [powershell]::Create().AddScript($scriptblock).AddArgument($chunk).AddArgument($pattern)
$runspace.RunspacePool = $runspace_pool
$null = $runspaces.Add([PSCustomObject]@{
pipe = $runspace
status = $runspace.BeginInvoke()
})
}
$all_results = foreach ($runspace in $runspaces)
{
$runspace.pipe.EndInvoke($runspace.status)
}
$runspace_pool.Close()
$runspace_pool.Dispose()
return $all_results
}
# Usage
$path = "."
$filter = "*.txt"
$pattern = "KETCHUP!"
$results = Search-Files -path $path -filter $filter -pattern $pattern
$results | ForEach-Object { Add-Content -Path 'C:\intel\matches.txt' -Value $_ }
您必须注意
[System.IO.File]::OpenText($file.FullName)
而不是 Select-String
上的替换。使用 System.IO.File
类可以更有效地使用内存,因为它逐行读取文件,这对于大文件特别有利。相比之下,Select-String
将整个文件加载到内存中,这可能会降低性能并且更占用内存。逐行方法还允许在找到匹配项后提前退出,从而进一步优化搜索过程。