为什么PowerShell多线程中没有正确处理条件语句



我正在尝试获取windows系统目录(C:\windows\System32(中的dll和exe文件名。

该路径中的文件太多,所以我尝试使用多线程。

然而,当比较(在"IF"语句中(由Addscript添加的ScriptBlock中的扩展时,只有"IF";"错误";结果出现,所以无法将文件名添加到列表中。

if ($extensionList -contains $tmpExtension.ToString())

也许条件语句是错误的,或者ScriptBlock是错误的?当不使用多线程时,条件语句正常工作。

整个代码(Powershell 5.1(:

$extensionList = @(".DLL",".EXE")
$systemDir = "C:WindowsSystem32"
$threadNumber = 4 #threads
$tempList = @()
$ScriptBlock = {
Param ($tmpFilePath)
$tmpExtension = [IO.Path]::GetExtension($tmpFilePath)
if ($extensionList -contains $tmpExtension.ToString()) {
Return $tmpFilePath
}
Return $null
}

$RunspacePool = [RunspaceFactory]::CreateRunspacePool(1, $threadNumber)
$RunspacePool.Open()
$Jobs = @()
$tmpfileNameList = (Get-ChildItem -Path $systemDir -File) | select-object Name
foreach($tmpfileName in $tmpfileNameList) {
$tmpFilePath = $systemDir + $tmpfileName.Name
$Job = [powershell]::Create().AddScript($ScriptBlock).AddArgument($tmpFilePath)
$Job.RunspacePool = $RunspacePool
$Jobs += New-Object PSObject -Property @{
Pipe = $Job
Result = $Job.BeginInvoke()
}
}
while ($Jobs.IsCompleted -contains $false) {Start-Sleep -Milliseconds 100}

$Results = @()
ForEach ($Job in $Jobs){
if ($Job.Pipe.EndInvoke($Job.Result) -ne $null) {
$tempList += $Job.Pipe.EndInvoke($Job.Result)
}
}
foreach($tmp in $tempList) {
Write-Host $tmp
}

主要问题是您的powershell实例不知道$extensionList是什么,因为它从未作为参数传递,因此它总是计算为:

$null -contains [IO.Path]::GetExtension($tmpFilePath)

如果您的意图是返回扩展名为.dll.exe的文件的FullName,那么您的scriptblock应该是这样的:

$ScriptBlock = {
Param ($tmpFilePath, $extensionList)
if ($extensionList -contains [IO.Path]::GetExtension($tmpFilePath))
{
$tmpFilePath
}
}

foreach循环看起来是这样的:

$tmpfileNameList = (Get-ChildItem -Path $systemDir -File).FullName
$params = @{
extensionList = $extensionList
}
$jobs = foreach($path in $tmpfileNameList)
{
$params.tmpFilePath = $path

$Job = [powershell]::Create().AddScript($ScriptBlock).AddParameters($params)
$Job.RunspacePool = $RunspacePool
[pscustomobject]@{
Pipe = $Job
Result = $Job.BeginInvoke()
}   
}

详见PowerShell.AddParameters Method

那么这一行就错了:

while ($Jobs.IsCompleted -contains $false) {
Start-Sleep -Milliseconds 100
}

也应该更改:

while ($Jobs.Result.IsCompleted -contains $false) {
Start-Sleep -Milliseconds 100
}

最后,您可以获得运行空间的结果,如:

$results = $jobs.ForEach({ $_.Pipe.EndInvoke($_.Result) })
PS /> $results

除了上述内容外,还需要改进逻辑,使脚本真正高效。

不应该向每个powershell实例传递一条路径,而应该将路径的array($tmpfileNameList(拆分为块,并为每个实例提供一个要处理的块。

你可以使用这样的东西来分割你的array:

$groupSize = [math]::Ceiling($tmpfileNameList.Count / $threadNumber)
$counter = [pscustomobject]@{ Value = 0 }
$groups = $tmpfileNameList | Group-Object -Property {
[math]::Floor($counter.Value++ / $groupSize)
}

在这种情况下,由于使用4线程,$groups将包含4个组。如果你想把这种技术应用到你的代码中,你可以从这个链接复制逻辑。

最新更新