Displaying progress during a cycle

I have a ForEach loop that I want to show progress:

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

      

I have tried using the write process in various places in the above code and cannot get it to work as it loops from 1 to 254.

+3


source to share


3 answers


Something like that?

$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++
}

      

Reference: Write-Progress Cmdlet :

The cmdlet Write-Progress

displays a progress bar in Windows A PowerShell Command Prompt window that displays the status of the current command or script. You can select the indicators displayed by the bar and the text that appears above and below the progress bar.



Edit : Rewriting the code above as a one-liner is easy: just highlight the individual commands with semicolons ( ;

) instead of line breaks:

$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++}

      

or easier with hardcoded 1..254

and 254

instead of $arr

and $all

respectively:

$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++}

      

+5


source


Josef Z. Helpful answer reveals a viable solution.

Your specific desire to get a progress bar by simply inserting a command Write-Progress

into an existing command script block is ForEach-Object

not possible:

The script block passed to ForEach-Object

cannot know in advance how many objects will be piped
, which is a prerequisite for displaying progress as a percentage.

You must determine the number of iterations beforehand.

If you want to generalize this, use a function like this (intentionally simplified):



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
  }
}

      

Again, note that all pipeline inputs are collected ahead of time.

In your case, you would call it like this:

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

      

+3


source


You can't really add a progress bar and store your code as a single statement, but you can do it in one line by separating the required commands with half-columns:

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++}

      

This uses a block -Begin

in the ForEach-Object command to initialize the counter $i

as 0. In the comments with this approach, we have to hard-code the shared collection, as there is no way to programmatically define it from the ForEach-Object loop because each iteration deals with one item from the collection ... This is mostly problematic because you are in multiple ranges where, as if you were a pipeline in a collection, we could use this collection property .count

to determine the total.

However, it's also worth noting that you don't need to display a progress bar to use Write-Progress

. You can simply use it to display a message to the user (no moving progress bar) that at least shows progress for now (just not that far from all the work you have set). This simplifies the code:

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

      

+1


source







All Articles