在PowerShell 7中try catch后访问变量出错



我在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

最新更新