在 For 循环期间显示进度

Showing Progress during For Loop

我有一个要显示进度的 ForEach 循环:

1..254 | ForEach-Object {Test-Connection -ErrorAction SilentlyContinue -count 
1 -TimeToLive 32 "$ipcut.$_"}
#^need to get a progress bar somewhere here^

我已经尝试在上面代码的各个地方使用 write-progress,但似乎无法正常工作,因为它从 1-254 循环。

是这样的吗?

$ipCut ='192.168.1'    ### not included in the original question

$arrTest = 1..254
$all = $arrTest.Count
$i = 0
$arrTest | ForEach-Object {
   Write-Progress -PercentComplete (
       $i*100/$all) -Activity "PINGs completed: $i/$all"  -Status 'Working'
   Test-Connection -ErrorAction SilentlyContinue -count 1 -TimeToLive 32 "$ipcut.$_"
   $i++
}

参考:Write-Progress cmdlet

The Write-Progress cmdlet displays a progress bar in a Windows PowerShell command window that depicts the status of a running command or script. You can select the indicators that the bar reflects and the text that appears above and below the progress bar.

编辑:将上面的代码重写为一行很容易:只需用分号 (;) 代替换行符分隔特定的命令:

$arr=1..254; $all=$arr.Count; $i=0; $arr|ForEach-Object{Write-Progress -PercentComplete ($i*100/$all) -Activity "PINGs completed: $i/$all"  -Status 'Working'; Test-Connection -ErrorAction SilentlyContinue -count 1 -TimeToLive 32 "$ipcut.$_"; $i++}

或更简单的硬编码 1..254254 而不是 $arr$all

$i=0; 1..254|ForEach-Object{Write-Progress -PercentComplete ($i*100/254) -Activity "PINGs completed: $i/254"  -Status 'Working'; Test-Connection -ErrorAction SilentlyContinue -count 1 -TimeToLive 32 "$ipcut.$_"; $i++}

您无法真正添加进度条并将代码保持为单个语句,但您可以通过用分号分隔所需的命令在一行中完成:

1..254 | ForEach-Object -Begin {$i = 0} -Process {Write-Progress -PercentComplete ($i/254*100) -Activity "Tests completed: $i/254" -Status "Testing $ipcut.$_"; Test-Connection -ErrorAction SilentlyContinue -count 1 -TimeToLive 32 "$ipcut.$_"; $i++}

这在 ForEach-Object 命令中使用 -Begin 块将计数器 $i 初始化为 0。根据评论,使用这种方法我们必须对集合的总数进行硬编码,因为无法从 ForEach-Object 循环中以编程方式确定它,因为每次迭代都处理集合中的单个项目。这主要是有问题的,因为你在一个数字范围内管道,就像你在一个集合中管道一样,我们可以使用该集合的 .count 属性 来确定总数。

但是,同样值得注意的是,您 根本没有 显示进度条来使用 Write-Progress。您可以只使用它向用户显示一条消息(没有移动的进度条),它至少仍然显示进度(只是不是您的总工作完成了多长时间)。这样做可以将代码简化为:

1..254 | ForEach-Object {Write-Progress -Activity "Testing $ipcut.$_"; Test-Connection -ErrorAction SilentlyContinue -count 1 -TimeToLive 32 "$ipcut.$_"}

显示了一个可行的解决方案。

您想要通过将 Write-Progress 命令插入现有命令的 ForEach-Object 脚本块来获得进度条的具体愿望是不可能的,但是:

传递给ForEach-Object的脚本块无法提前知道将通过管道传递多少对象,这是以 百分比.

显示进度的先决条件

必须确定迭代次数提前

如果你想泛化这个,使用函数,如下(故意保持简单):

function Invoke-WithProgress {
  param(
    [scriptblock] $Process,
    [string]      $Activity = 'Processing'
  )
  # Collect the pipeline input ($Input) up front in an array, 
  # using @(...) to force enumeration.
  $a = @($Input)
  # Count the number of input objects.
  $count = $a.Count; $i = 0
  # Process the input objects one by one, with progress display.
  $a | ForEach-Object {
    Write-Progress -PercentComplete ((++$i)*100/$count) -Activity "$Activity $i/$count..."
    . $Process
  }
}

再次注意,所有管道输入都已收集预先

在您的情况下,您将按如下方式调用它:

1..254 | Invoke-WithProgress {
 Test-Connection -ErrorAction SilentlyContinue -count 1 -TimeToLive 32 "$ipcut.$_"
}