How can I use Write-Progress in the Pipeline function?

I am trying to write a function in PowerShell that accepts pipeline input. I want to display a progress bar with Write-Progress , which is incremented for each item in the pipeline.

For instance:

 function Write-PipelineProgress { [Cmdletbinding()] Param ( [Parameter(Position=0, Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)] ` [object[]] $Input, [string] $Activity = "Processing items" ) Begin { Write-Progress -Activity $Activity -Status "Preparing" } Process { # how do i determine how much progress we've made? $percentComplete = ... ? Write-Progress -Activity $Activity -Status "Working" -PercentComplete $percentComplete # return current item, so processing can continue $_ } End { Write-Progress -Activity $Activity -Status "End" -Completed } } Get-ChildItem | Write-PipelineProgress -Activity "Listing files" 

How can I determine the progress (% complete)?

+4
source share
3 answers

You need to know the number of items in the pipeline to track progress.

Powershell 3.0 can give you an account of what's under development, without having to do any work outside of access to the .Count $Input property if the pipeline parameter is correctly declared. It also means that you can end the begin {} process {} end {} blocks and just have a direct function if you want.

Earlier versions do not have the Count property, so you first need to iterate and grab the pipeline to get the count, and then process it again, which is not as efficient as I will show later.

Powershell V2 Version:

 function Show-ProgressV2{ param ( [Parameter(Mandatory=$true, Position=0, ValueFromPipeline=$true)] [PSObject[]]$InputObject, [string]$Activity = "Processing items" ) Begin {$PipeArray = @()} Process {$PipeArray+=$InputObject} End { [int]$TotItems = ($PipeArray).Count [int]$Count = 0 $PipeArray|foreach { $_ $Count++ [int]$percentComplete = [int](($Count/$TotItems* 100)) Write-Progress -Activity "$Activity" -PercentComplete "$percentComplete" -Status ("Working - " + $percentComplete + "%") } } } 

Powershell V3 Version:

 function Show-ProgressV3{ [CmdletBinding()] param ( [Parameter(Mandatory=$true, Position=0, ValueFromPipeline=$true)] [PSObject[]]$InputObject, [string]$Activity = "Processing items" ) [int]$TotItems = $Input.Count [int]$Count = 0 $Input|foreach { $_ $Count++ [int]$percentComplete = ($Count/$TotItems* 100) Write-Progress -Activity $Activity -PercentComplete $percentComplete -Status ("Working - " + $percentComplete + "%") } } 

Efficiency
Comparing two V3 functions is about 5-6 times faster, depending on the size of the conveyor.

Consider the following pre-filtered list of files, looking for all .jpg files on my home drive, selecting the first 200 files, sort and print using the function 3 times in the pipe, for your comment:

 $V2 = Measure-Command {Get-ChildItem -Filter *.jpg -Recurse ` | Show-ProgressV2 -Activity "Selecting" ` | Select-Object -First 200 ` | Show-ProgressV2 -Activity "Sorting" ` | Sort-Object -Property FullName ` | Show-ProgressV2 -Activity "Listing" ` | FL} $V3 = Measure-Command {Get-ChildItem -filter *.jpg -Recurse ` | Show-ProgressV3 -Activity "Selecting" ` | Select-Object -First 200 ` | Show-ProgressV3 -Activity "Sorting" ` | Sort-Object -Property FullName ` | Show-ProgressV3 -Activity "Listing" ` | FL} $V2 $V3 

Which gives me the following timings:

 PS C:\Users\Graham> C:\Users\Graham\Documents\Stack_ShowProgress_Pipeline.ps1 Days : 0 Hours : 0 Minutes : 0 Seconds : 48 Milliseconds : 360 Ticks : 483607111 TotalDays : 0.000559730452546296 TotalHours : 0.0134335308611111 TotalMinutes : 0.806011851666667 TotalSeconds : 48.3607111 TotalMilliseconds : 48360.7111 Days : 0 Hours : 0 Minutes : 0 Seconds : 8 Milliseconds : 335 Ticks : 83358374 TotalDays : 9.6479599537037E-05 TotalHours : 0.00231551038888889 TotalMinutes : 0.138930623333333 TotalSeconds : 8.3358374 TotalMilliseconds : 8335.8374 
+5
source

You need to decide how you will show progress. In this example, I arbitrarily decided that the initial block would take 5%, the process block would take 90%, and the final block would take 5%. From there, you can display intermediate percentages (from 0% to 5% in the start block, for example) in an arbitrary way (which this example does in the start and end blocks). Or you can specify percentages on the amount of processing the script is actually (which makes the process block). Note that this example more or less makes no sense (but it will execute and show a progress bar). Also note that the script, as written, will not work in the pipeline, so the begin / process / end blocks are really not needed, but they do not damage.

 function progress-example { param( [string[]] $files) begin { # This code is somewhat arbitrarily considering the begin block to # take 5% of the time of this cmdlet. The process block is # considered to take 90% of the time, and the end block the last 5% write-progress -Activity "Progress Example" -status Beginning ` -CurrentOperation "Initializing sorted script table" -PercentComplete 0 # This could be any kind of initialization code; # The code here is non-sense $mySortedScriptTable = @{} ls *.ps1 | % { $mySortedScriptTable[$_.name] = cat $_ | sort } sleep 2 # this code executes too quickly to see the progress bar so slow it down write-progress -Activity "Progress Example" -status Beginning ` -CurrentOperation "Initializing script size table" -PercentComplete 3 $mySortedScriptSizeTable = @{} ls *.ps1 | % { $mySortedScriptSizeTable[$_.name] = $_.Length } $totalSizeRequested = 0 foreach ($file in $files) { $totalSizeRequested += $mySortedScriptSizeTable[$file] } $numberCharsProcessed = 0 sleep 2 # this code executes too quickly to see the progress bar so slow it down write-progress -Activity "Progress Example" -status Beginning ` -CurrentOperation "Initialization complete" -PercentComplete 5 sleep 2 # this code executes too quickly to see the progress bar so slow it down } process { foreach ($file in $files) { $thisFileSize = $mySortedScriptSizeTable[$file] # Process block takes 90% of the time to complete (90% is arbitrary) $percentProcess = 90 * ($numberCharsProcessed / $totalSizeRequested) write-progress -Activity "Progress Example" -status "Processing requested files" ` -CurrentOperation "Starting $file" -PercentComplete (5 + $percentProcess) "File $file requested. Size = $thisFileSize. Sorted content follows" $mySortedScriptTable[$file] $numberCharsProcessed += $thisFileSize $percentProcess = 90 * ($numberCharsProcessed / $totalSizeRequested) write-progress -Activity "Progress Example" -status "Processing requested files" ` -CurrentOperation "Starting sleep after $file" -PercentComplete (5 + $percentProcess) sleep 2 # slow it down for purposes of demo } } end { write-progress -Activity "Progress Example" -status "Final processing" ` -CurrentOperation "Calculating the fun we had with this example" -PercentComplete (95) sleep 2 write-progress -Activity "Progress Example" -status "Final processing" ` -CurrentOperation "It looking like we had mucho fun" -PercentComplete (97) sleep 2 write-progress -Activity "Progress Example" -status "Final processing" ` -CurrentOperation "Yes, a lot of fun was had" -PercentComplete (99) sleep 1 } } progress script1.ps1, script2.ps1, script3.ps1 
+1
source

I publish the Write-ProgressEx cmdlet to avoid the usual code. Give it a try.

Project https://github.com/mazzy-ax/Write-ProgressEx


I want to display a progress bar with Write-Progress, which is incremented for each item in the pipeline.

Use the Write-ProgressEx -Increment switch.

Repository cmdLet Total and Current . It automatically calculates PercentComplete and SecondsRemaining .

How can I determine the progress (% complete)?

  • The Write-ProgressEx calcaulte -PercentComplete cmdlet is automatic if the developer provides Total and Current . The increment of the Current cmdlet if the -Increment parameter is specified.

  • The developer can use Get-ProgressEx to get the actual parameter values.

See examples https://github.com/mazzy-ax/Write-ProgressEx/tree/master/samples


More details :

Write-ProgressEx extends the functionality of the standard powershell cmdlet. It provides an easy way to use the -PercentComplete and -SecondsRemaining keys.

Cmdlet:

  • works with a pipe;
  • works with an empty activity line;
  • automatically displays totals;
  • automatically calculates interest;
  • uses [system.diagnostic.stopwatch] to calculate repeat seconds;
  • completes all internal progressions if there are no parameters;
  • stores total values, current values ​​and actual parameters in the module hash table;
  • provide get / set cmdlets to access actual parameters

Note. The cmdlet is unsafe with multithreading.

+1
source

Source: https://habr.com/ru/post/1491027/


All Articles