我在PowerShell 7上收到这个错误:
Total Urls to process: 12496
i: 1 | total:
RuntimeException:
Line |
45 | $percent = [int](100 * $i / $lines)
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| Attempted to divide by zero.
开发这个脚本:
$srcfile = "C:UserswnuneOneDriveEscritorioimagenescardlist.txt"
$urls = Get-Content $srcfile
$lines = 0
switch -File $srcfile { default { ++$lines } }
Write-Host "Total Urls to process: $lines "
$i = 0
Write-Progress -Activity "Downloading files" -Status "In progress" -PercentComplete $i;
$urls | ForEach-Object -Parallel {
$url = $_
try {
$filename = Split-Path $url -Leaf
$destination = "C:UserswnuneOneDriveEscritorioimagenes$filename"
$ProgressPreference = 'SilentlyContinue'
$response = Invoke-WebRequest -Uri $url -ErrorAction SilentlyContinue
if ($response.StatusCode -ne 200) {
Write-Warning "============================================="
Write-Warning "Url $url return Error. "
continue
}
if (Test-Path $destination) {
Write-Warning "============================================="
Write-Warning "File Exist in Destination: $filename "
continue
}
$job = Start-BitsTransfer -Source $url -Destination $destination -Asynchronous
while (($job | Get-BitsTransfer).JobState -eq "Transferring" -or ($job | Get-BitsTransfer).JobState -eq "Connecting")
{
Start-Sleep -m 250
}
Switch(($job | Get-BitsTransfer).JobState)
{
"Transferred" {
Complete-BitsTransfer -BitsJob $job
}
"Error" {
$job | Format-List
}
}
}
catch
{
Write-Warning "============================================="
Write-Warning "There was an error Downloading"
Write-Warning "url: $url"
Write-Warning "file: $filename"
Write-Warning "Exception Message:"
Write-Warning "$($_.Exception.Message)"
}
$i++
Write-Host "i: $i | total: $lines"
$percent = [int](100 * $i / $lines)
Write-Progress -Activity "Downloading files" -Status "In progress" -PercentComplete $percent
}
Write-Progress -Activity "Downloading files" -Status "Completed" -Completed
假设这是因为我正在实现-并行尝试使用同步对象:
$syncObject = [System.Object]::new()
$srcfile = "C:UserswnuneOneDriveEscritorioimagenescardlist.txt"
$urls = Get-Content $srcfile
$lines = 0
switch -File $srcfile { default { ++$lines } }
Write-Host "Total Urls to process: $lines "
$i = 0
Write-Progress -Activity "Downloading files" -Status "In progress" -PercentComplete $i;
$urls | ForEach-Object -Parallel {
[System.Threading.Monitor]::Enter($syncObject)
try {
$url = $_
$filename = Split-Path $url -Leaf
$destination = "C:UserswnuneOneDriveEscritorioimagenes$filename"
$ProgressPreference = 'SilentlyContinue'
$response = Invoke-WebRequest -Uri $url -ErrorAction SilentlyContinue
if ($response.StatusCode -ne 200) {
Write-Warning "============================================="
Write-Warning "Url $url return Error. "
continue
}
if (Test-Path $destination) {
Write-Warning "============================================="
Write-Warning "File Exist in Destination: $filename "
continue
}
$job = Start-BitsTransfer -Source $url -Destination $destination -Asynchronous
while (($job | Get-BitsTransfer).JobState -eq "Transferring" -or ($job | Get-BitsTransfer).JobState -eq "Connecting")
{
Start-Sleep -m 250
}
Switch(($job | Get-BitsTransfer).JobState)
{
"Transferred" {
Complete-BitsTransfer -BitsJob $job
}
"Error" {
$job | Format-List
}
}
}
catch
{
Write-Warning "============================================="
Write-Warning "There was an error Downloading"
Write-Warning "url: $url"
Write-Warning "file: $filename"
Write-Warning "Exception Message:"
Write-Warning "$($_.Exception.Message)"
}
finally
{
$i++
Write-Host "i: $i | total: $lines"
$percent = [int](100 * $i / $lines)
Write-Progress -Activity "Downloading files" -Status "In progress" -PercentComplete $percent
[System.Threading.Monitor]::Exit($syncObject)
}
}
Write-Progress -Activity "Downloading files" -Status "Completed" -Completed
得到另一个错误:
Total Urls to process: 12496
MethodInvocationException:
Line |
2 | [System.Threading.Monitor]::Enter($syncObject)
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| Exception calling "Enter" with "1" argument(s): "Value cannot be null."
我不知道什么是错的,或者为什么会发生这种情况,如果我尊重执行的顺序,我从一开始就创建同步对象;有人能帮我一下吗?
的主要思想是处理12496个url…并根据并行处理的url数量创建进度条。
PowerShell v7+ForEach-Object
-Parallel
特性使用独立的、基于线程的运行空间并行执行代码。
在任何PowerShell代码(脚本块,{ ... }
)执行的运行空间,你必须使用$using:
作用域的顺序引用调用者作用域中的变量值.
- 这个答案提供了所有需要
$using:
的上下文的概述。
一个简单的例子:
$i = 42
1..3 |
ForEach-Object -Parallel {
[pscustomobject] @{
'Thread-local $i' = $i
'$using:i value' = $using:i
}
}
输出:
Thread-local $i $using:i value
--------------- --------------
42
42
42
可以看到,$i
变量没有值,因为它引用了一个尚未初始化的线程局部(runspace-local)变量。
在调用者作用域中更新值:
$using:
引用只是调用者作用域中变量的值,而不是变量本身。
如果值恰好是。net引用类型的实例,您可以(潜在地)通过设置它们的属性和/或调用它们的方法来更新它们,但是对于恰好是值类型的实例的值,例如[int]
,不能工作。
为了更新后者的实例,将它们包装在引用类型中,例如哈希表。
为了使更新成为值线程安全的,需要显式同步,例如通过System.Threading.Monitor
:谢谢,Santiago square .
$iWrapper = @{ Value = 42 }
1..3 |
ForEach-Object -Parallel {
# Lock the hashtable so that no other thread can update it.
[System.Threading.Monitor]::Enter($using:iWrapper)
# Update its (one and only) entry.
(++($using:iWrapper).Value)
# Release the lock.
[System.Threading.Monitor]::Exit($using:iWrapper)
}
输出:
43
44
45