问题描述
$files = 1..100
$i = 0
$files | Foreach-Object {
$progress = ("#" * $i)
Write-Host "`r$progress" -NoNewLine
$i ++
Start-Sleep -s 0.1
}
但是如果我想同时并行做两件事,我无法输出进度,因为我无法在并行循环之外更改变量。这不符合要求:
$files = 1..100
$i = 0
$files | Foreach-Object -ThrottleLimit 2 -Parallel {
$progress = ("#" * $i)
Write-Host "`r$progress" -NoNewLine
$i ++
Start-Sleep -s 0.1
}
我找不到一个好的解决方案来访问外部变量,不仅要使用 $Using 读取它,还要更改它。这在 Powershell 7 中甚至是可能的吗?
解决方法
根据本文 - PowerShell ForEach-Object Parallel Feature - 您可以使用 $using
关键字从“外部”脚本中引用变量:
例如
$files = 1..100
$i = 100;
$files | Foreach-Object -ThrottleLimit 2 -Parallel {
write-host ($using:i)
Start-Sleep -s .1
}
# 100
# 100
# etc
但是如果您尝试更新该值,您将得到:
$files | Foreach-Object -ThrottleLimit 2 -Parallel {
$using:i += $using:i
Start-Sleep -s .1
}
ParserError:
Line |
2 | $using:i += $using:i
| ~~~~~~~~
| The assignment expression is not valid. The input to an assignment operator must be an object that is able to accept
| assignments,such as a variable or a property.
基本上,您不能将 back 分配给 $using:i
变量。
你可以做的是改变一个复杂对象的属性——例如这个:
$counter = @{ "i" = 0 }
$files | Foreach-Object -ThrottleLimit 2 -Parallel {
($using:counter).i = ($using:counter).i + 1
Start-Sleep -s .1
}
$counter
# Name Value
# ---- -----
# i 100
#
它允许您更新值,但可能不是(可能不会)是线程安全的。
,我认为这是正确的,基于 Writing Progress across multiple threads with Foreach Parallel。 It may be missing a lock,但仅就写作进度而言,这可能没什么大不了的。在这种情况下,您也可以使用文件名作为进度。
$hash = @{i = 1}
$sync = [System.Collections.Hashtable]::Synchronized($hash)
$files = 1..100
$files | Foreach-Object -throttlelimit 2 -parallel {
$syncCopy = $using:sync
$progress = '#' * $syncCopy.i
#$progress = '#' * $_
Write-Host "`r$progress" -NoNewLine
$syncCopy.i++
Start-Sleep .1
}
输出:
####################################################################################################