Multithreading 与使用单线程脚本相比,使用PowerShell多线程减少了输出

Multithreading 与使用单线程脚本相比,使用PowerShell多线程减少了输出,multithreading,powershell,Multithreading,Powershell,我正在Windows 7桌面上使用PowerShell 2.0。我正在尝试在企业CIFS共享中搜索关键字/regex。我已经有一个简单的单线程脚本,可以做到这一点,但一个关键字需要19-22个小时。我已经根据Surly Admin的文章创建了一个多线程脚本,这是多线程的第一次尝试 以及与这些帖子相关的链接 我决定使用运行空间而不是后台作业,因为主流观点认为这样更有效。问题是,我只得到了我拥有的多线程脚本的部分结果输出。不确定它是I/O还是内存,还是其他什么。希望这里有人能帮忙。这是代码 cl

我正在Windows 7桌面上使用PowerShell 2.0。我正在尝试在企业CIFS共享中搜索关键字/regex。我已经有一个简单的单线程脚本,可以做到这一点,但一个关键字需要19-22个小时。我已经根据Surly Admin的文章创建了一个多线程脚本,这是多线程的第一次尝试

以及与这些帖子相关的链接

我决定使用运行空间而不是后台作业,因为主流观点认为这样更有效。问题是,我只得到了我拥有的多线程脚本的部分结果输出。不确定它是I/O还是内存,还是其他什么。希望这里有人能帮忙。这是代码

cls
Get-Date
Remove-Item C:\Users\user\Desktop\results.txt

$Throttle = 5 #threads

$ScriptBlock = {
    Param (
        $File
    )
    $KeywordInfo = Select-String -pattern KEYWORD -AllMatches -InputObject $File
    $KeywordOut = New-Object PSObject -Property @{
        Matches = $KeywordInfo.Matches
        Path = $KeywordInfo.Path
    }
    Return $KeywordOut
}

$RunspacePool = [RunspaceFactory]::CreateRunspacePool(1, $Throttle)
$RunspacePool.Open()
$Jobs = @()

$Files = Get-ChildItem -recurse -erroraction silentlycontinue
ForEach ($File in $Files) {
    $Job = [powershell]::Create().AddScript($ScriptBlock).AddArgument($File)
    $Job.RunspacePool = $RunspacePool
    $Jobs += New-Object PSObject -Property @{
        File = $File
        Pipe = $Job
        Result = $Job.BeginInvoke()
    }
}

Write-Host "Waiting.." -NoNewline
Do {
    Write-Host "." -NoNewline
    Start-Sleep -Seconds 1
} While ( $Jobs.Result.IsCompleted -contains $false)
Write-Host "All jobs completed!"

$Results = @()
ForEach ($Job in $Jobs) {
    $Results += $Job.Pipe.EndInvoke($Job.Result)
    $Job.Pipe.EndInvoke($Job.Result) | Where {$_.Path} | Format-List | Out-File -FilePath C:\Users\user\Desktop\results.txt -Append -Encoding UTF8 -Width 512
}

Invoke-Item C:\Users\user\Desktop\results.txt
Get-Date
这是我正在使用的有效的单线程版本,包括我用于社交的正则表达式

cls
Get-Date

Remove-Item C:\Users\user\Desktop\results.txt

$files = Get-ChildItem -recurse -erroraction silentlycontinue

ForEach ($file in $files) {
    Select-String -pattern '[sS][sS][nN]:*\s*\d{3}-*\d{2}-*\d{4}' -AllMatches -InputObject $file | Select-Object matches, path |
        Format-List | Out-File -FilePath C:\Users\user\Desktop\results.tx -Append -Encoding UTF8 -Width 512
}

Get-Date
Invoke-Item C:\Users\user\Desktop\results.txt

我希望随着时间的推移建立这个答案,因为我不想过度评论。我还不知道为什么多线程会丢失数据,但我认为我们可以通过更新正则表达式来提高性能。首先,你有很多贪婪的量词,我认为我们可以缩小

[sS][sS][nN]:*\s*\d{3}-*\d{2}-*\d{4}

默认情况下,“选择字符串”不区分大小写,因此不需要开头的部分。您必须检查多个冒号吗?因为您正在查找0个或多个
。连字符也是如此。也许这些会更好吗?哪个匹配0或1

ssn:?\s*\d{3}-?\d{2}-?\d{4}

这是假设您正在寻找大部分格式正确的SSN。如果人们把它们隐藏在文本中,那么您可能还需要寻找其他分隔符

我还建议将文本添加到单独的文件中,并在执行后将其合并。如果没有别的只是为了测试


希望这将是正确解决方案的开始。

结果表明,出于某种原因,Select String cmdlet在多线程处理方面出现了问题。我没有足够的开发人员背景,无法说出幕后发生了什么。然而,我确实发现,通过使用selectstring中的-quiet选项,将其转换为布尔输出,我能够得到我想要的结果

每个文档中的第一个模式匹配给出了一个真值。当我得到一个true时,我将文档的路径返回到一个数组。完成后,我将针对从scriptblock输出的路径运行模式匹配。这并不像我所希望的那样在性能方面非常有效,但与单线程相比仍然有相当大的改进

我遇到的另一个问题是在每个阶段都试图将结果输出到文档,从而导致对磁盘的读/写。我已将其更改为数组。虽然仍然需要大量内存,但速度要快得多

下面是生成的代码。如有任何关于性能改进的其他提示,敬请告知:

cls
Remove-Item C:\Users\user\Desktop\output.txt

$Throttle = 5 #threads

$ScriptBlock = {
   Param (
      $File
   )
   $Match = Select-String -pattern 'ssn:?\s*\d{3}-?\d{2}-?\d{4}' -Quiet -InputObject $File
   if ( $Match -eq $true ) {
        $MatchObjects = Select-Object -InputObject $File
        $MatchOut = New-Object PSObject -Property @{
            Path = $MatchObjects.FullName
        }
   }
   Return $MatchOut
}

$RunspacePool = [RunspaceFactory]::CreateRunspacePool(1, $Throttle)
$RunspacePool.Open()
$Jobs = @()

$Files = Get-ChildItem -Path I:\ -recurse -erroraction silentlycontinue
ForEach ($File in $Files) {
   $Job = [powershell]::Create().AddScript($ScriptBlock).AddArgument($File)
   $Job.RunspacePool = $RunspacePool
   $Jobs += New-Object PSObject -Property @{
      File = $File
      Pipe = $Job
      Result = $Job.BeginInvoke()
   }
}

$Results = @()
ForEach ($Job in $Jobs) {
    $Results += $Job.Pipe.EndInvoke($Job.Result)
}

$PathValue = @()
ForEach ($Line in $Results) {
    $PathValue += $Line.psobject.properties | % {$_.Value}
}

$UniqValues = $PathValue  | sort | Get-Unique

$Output = ForEach ( $Path in $UniqValues ) {
    Select-String -Pattern '\d{3}-?\d{2}-?\d{4}' -AllMatches -Path $Path | Select-Object -Property Matches, Path
}

$Output | Out-File -FilePath C:\Users\user\Desktop\output.txt -Append -Encoding UTF8 -Width 512

Invoke-Item C:\Users\user\Desktop\output.txt

我在脚本块
中看到Return$SsnOut
,但我看不到
$SsnOut
在任何地方填充。应该是
$KeywordOut
吗?正确,应该是$KeywordOut。我已经编辑了代码以反映更改。那么这些文件是否有异常?这就是为什么要花这么长时间来处理?这实际上就是你要寻找的模式吗?不,是寻找社会保险号码、坏文件(exe、ps1、bat、py等)。有些文件很大,但它也是一个庞大的目录结构。我希望它更快,但不会以丢失文件为代价。我使用一个名为fileseek的程序来搜索信用卡号码和其他东西。也许你可以看看。为了留在powershell世界,您是否使用正则表达式进行这些查询?也许我们可以在那里刮胡子。我本来打算用这个来回应的,但是我认为贪婪在这里并不重要对于性能来说是可怕的。匹配所有导致可能性爆炸的因素,但是提供的正则表达式只使用-,而不是。因此,我们只匹配一个0或更多dash的序列。此建议的另一个问题是,它无法匹配以前正则表达式没有匹配的部分内容(这可能是一个问题,也可能不是一个问题,取决于以前的数据有多干净)。例如,原始正则表达式匹配eh number中使用的双破折号,而建议的正则表达式不匹配。(例如,
'ssn:123--12-1111'
)。如果源数据包含带双破折号的坏数据,或者“ssn”和实际数字之间有多个空格,则使用原始正则表达式。否则,上述建议会更好。有许多事情可能无法解释差异。基于我的评论“这是假设您正在寻找大多数正确格式的SSN”,我想简明扼要。如果没有其他内容,您甚至可以选择第一部分<代码>(ssn:?)?取决于Op使用的是什么。谢谢你的关心。vs-就贪婪而言。感谢您提供正则表达式的解决方案。我对脚本和正则表达式相当陌生,因此这应该有助于提高性能。我明天要考试。我看到的结果只使用了一个冒号,可能有也可能没有连字符,但有些结果确实有多个空格,但看起来@Matt regex solution适合我。我不确定它是否会给这种情况增加一些线索,但当我运行多线程版本时,我得到了大约10%-15%的返回结果,如果我添加更多线程,返回的结果会更低。上面的数字只是估计。明天我会得到更好的数据。谢谢大家。