Powershell - 将数组作为作业(本地/多线程)

时间:2016-10-07 10:18:47

标签: arrays multithreading powershell jobs

我试图解析网站以收集价格和产品详情。该脚本在循环中工作,但它非常慢。因此,我尝试将多线程PowerShell脚本作为一项工作运行。

我已经尝试了很多建议,但即使我能看到它的工作(网页请求屏幕闪烁),我也很难得到结果

我只选择了最后10名,但我稍后会加油门。只是无法将其输出。基本上我希望所有结果都能回流到$ arr。

#Import Danmurphy Sitelist
[xml] $XmlDocument = (New-Object System.Net.WebClient).DownloadString("http://www.example.com/sites.xml")

#get websites listed
$ImportedProducts = $XmlDocument.DocumentElement.url | select -Last 10

"Killing existing jobs . . ."
Get-Job | Remove-Job -Force
"Done."

#loop through the products

#Create Array
$arr = @()

#$argumentlist 

#ScriptBlock
$ScriptBlock = {
Param($product,$arr)

if ($product.loc -like "http://www.example.com/product/*"){

$uri = $product.loc
$WebResponse = Invoke-WebRequest -Uri $uri -SessionVariable WS 


#mainpricetest
$mainprice = $WebResponse.AllElements | ? { $_.Class -eq 'price-main' } | select innerText

$MainPriceArray = $mainprice.innerText.Split(' ')

$MainUnitArry = $MainPriceArray[1..10]

$MainDollar = $MainPriceArray[0]

$MainUnit = $MainUnitArry -join ' '


$item = New-Object PSObject
$item | Add-Member -type NoteProperty -Name 'Product Site' -Value $($product.loc)
$item | Add-Member -type NoteProperty -Name 'Main Price' -Value $($MainDollar)
$item | Add-Member -type NoteProperty -Name 'Main Unit' -Value $($MainUnit)



$arr += $item

}
}

foreach ($product in $ImportedProducts){
Start-Job -InputObject $ImportedProducts -ScriptBlock $ScriptBlock -ArgumentList $product,$arr
}

$data = Get-Job * | Receive-Job 

#Show Array
$arr

2 个答案:

答案 0 :(得分:1)

所以你会想要使用运行空间。 Runspaces是一件相当复杂的事情,幸运的是我们有Posh-RSJob为你处理一切。 https://github.com/proxb/PoshRSJob

您可以传入脚本块,因此您只需要很少的调整。 可能是这样的:

foreach ($product in $ImportedProducts){
    Start-RSJob -ScriptBlock $ScriptBlock
}
Get-RSjob | Receive-RSJob

答案 1 :(得分:1)

如果要将结果输入$ arr,则无法在脚本块中执行此操作。不能允许多个并行运行的脚本块访问变量的单个副本,而无需采取额外的步骤。

您的问题的答案是将每个脚本块的输出写为常规输出。该输出被缓冲,直到您使用Receive-Job从作业中获取结果,此时您以单线程方式将其捕获到$ arr变量中。下面是鳕鱼,它应该让你在那里的大部分。

#Import Danmurphy Sitelist
[xml] $XmlDocument = (New-Object System.Net.WebClient).DownloadString("http://www.example.com/sites.xml")

#get websites listed
$ImportedProducts = $XmlDocument.DocumentElement.url | select -Last 10

"Killing existing jobs . . ."
Get-Job | Remove-Job -Force
"Done."

#loop through the products

#Create Array
$arr = @()

#$argumentlist 

#ScriptBlock
$ScriptBlock = {
    Param($product)

    if ($product.loc -like "http://www.example.com/product/*"){

    $uri = $product.loc
    $WebResponse = Invoke-WebRequest -Uri $uri -SessionVariable WS 


    #mainpricetest
    $mainprice = $WebResponse.AllElements | ? { $_.Class -eq 'price-main' } | select innerText

    $MainPriceArray = $mainprice.innerText.Split(' ')

    $MainUnitArry = $MainPriceArray[1..10]

    $MainDollar = $MainPriceArray[0]

    $MainUnit = $MainUnitArry -join ' '


    $item = New-Object PSObject
    $item | Add-Member -type NoteProperty -Name 'Product Site' -Value $($product.loc)
    $item | Add-Member -type NoteProperty -Name 'Main Price' -Value $($MainDollar)
    $item | Add-Member -type NoteProperty -Name 'Main Unit' -Value $($MainUnit)



    Write-Output $item

    }
}

foreach ($product in $ImportedProducts){
    Start-Job -InputObject $ImportedProducts -ScriptBlock $ScriptBlock -ArgumentList $product
}

do {
    $arr += Get-Job -State Completed | Receive-Job -AutoRemoveJob
} while (Get-Job -State Running)

#Show Array
$arr