i received this error on PowerShell 7:
Total Urls to process: 12496
i: 1 | total:
RuntimeException:
Line |
45 | $percent = [int](100 * $i / $lines)
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| Attempted to divide by zero.
development this script:
$srcfile = "C:\Users\wnune\OneDrive\Escritorio\imagenes\cardlist.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:\Users\wnune\OneDrive\Escritorio\imagenes\$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
Assuming this is because I'm implementing -Parallel try using a sync object:
$syncObject = [System.Object]::new()
$srcfile = "C:\Users\wnune\OneDrive\Escritorio\imagenes\cardlist.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:\Users\wnune\OneDrive\Escritorio\imagenes\$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
get another error:
Total Urls to process: 12496
MethodInvocationException:
Line |
2 | [System.Threading.Monitor]::Enter($syncObject)
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| Exception calling "Enter" with "1" argument(s): "Value cannot be null."
I have no idea what is wrong or why this happens, if I am respecting the order of execution and I am creating the synchronization object from the beginning; Can someone help me with this???
the main idea is to process the 12496 Urls... and create a progress bar based on the number of Urls processed in parallel.
The PowerShell v7+
ForEach-Object
-Parallel
feature uses separate, thread-based runspaces to execute code in parallel.As in any PowerShell code (script block,
{ ... }
) that executes out of runspace, you must use the$using:
scope in order to refer to variable values in the caller's scope.$using:
is required.A simple example:
Output:
As you can see, the
$i
variable has no value, because it refers to a thread-local (runspace-local) variable that hasn't been initialized.Updating values in the caller's scope:
$using:
references are only ever the values of variables from the caller's scopes, not the variables themselves.If values happen to be instance of .NET reference types, you can (potentially) update them, by setting their properties and/or calling their methods, but this does not work for values that happen to be instances of value types, such as
[int]
.In order to update instances of the latter, wrap them in a reference type, such as a hashtable.
In order to make updating a value thread-safe, explicit synchronization is required, such as via
System.Threading.Monitor
:Thanks, Santiago Squarzon.Output: