3

I have a ForEach Loop that I want to show progress on:

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 write-progress in various places in the above code, and can't seem to get it working as it loops from 1-254.

0

3 Answers 3

5

Something like this?

$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 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.

Edit: rewriting above code as a one-liner is easy: just separate particular commands by a semicolon (;) 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 simpler with hard-coded 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++}
Sign up to request clarification or add additional context in comments.

Comments

3

Josef Z.'s helpful answer shows a viable solution.

Your specific desire to get a progress bar simply by inserting a Write-Progress command into your existing command's ForEach-Object script block is impossible, however:

The script block passed to ForEach-Object has no way of knowing in advance how many objects will be passed through the pipeline, which is a prerequisite for showing progress in terms of percentages.

You must determine the iteration count ahead of time.

If you want to generalize this, use a function, such as the following (purposely kept simple):

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 input is collected up front.

In your case, you'd invoke it as follows:

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

Comments

1

You can't truly add a progress bar and keep your code as a single statement, but you can do it on a single line by separating the needed commands via semi-colons:

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 -Begin block in the ForEach-Object command to initialize the counter $i as 0. Per the comments, with this approach we have to hardcode the total of the collection as there's no way to determine it programmatically from within the ForEach-Object loop, because each iteration deals with a single item from the collection. This is mostly problematic because you are piping in a number range, where as if you were piping in a collection we could use that collection's .count property to determine the total.

However, its also worth noting that you don't have to display a progress bar at all to use Write-Progress. You can just use it to display a message to the user (with no moving progress bar) which does at least still demonstrate progress (just not how far through the total job you are). Doing this simplifies the code to:

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.