Last active
April 8, 2023 22:11
-
-
Save mavaddat/814c3a2b8750fd8f659c72d28903cf96 to your computer and use it in GitHub Desktop.
Write Progress Template for PowerShell
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$Script:Timeout = 30 | |
# Suppose we need to do work on a list of files, and we want to show the user a progress bar | |
$files = Get-ChildItem -File | |
[scriptblock]$task = { | |
[CmdletBinding()] | |
param ( | |
[Parameter()] | |
[System.IO.FileSystemInfo] | |
$File | |
) | |
Get-Content -Path ($File.FullName) | Select-String -Pattern '(?<=foo)bar' -AllMatches | ForEach-Object { $_.Line } | Select-Object -Unique | Measure-Object | Select-Object -ExpandProperty Count | |
} | |
# First calculate the time to complete one task in the list | |
$largestFile = $files | Sort-Object -Property Length -Descending | Select-Object -First 1 | |
$start = [datetime]::Now | |
# Execute the task on the largest file | |
$task.Invoke($largestFile) | |
$ticksPerByteEstimate = ([datetime]::Now - $start).Ticks / $largestFile.Length # We will use this to estimate the time remaining for the entire list | |
$totalBytes = $files | ForEach-Object { $_.Length } | Measure-Object -Sum | Select-Object -ExpandProperty Sum | |
$estimate = [pscustomobject]@{ | |
TicksPerByte = $ticksPerByteEstimate; | |
BytesProcessed = $largestFile.Length; | |
TotalTicksExpected = $totalBytes * $ticksPerByteEstimate; | |
} | |
<# Do the tasks here ... #> | |
# Example | |
foreach($file in $files){ | |
# Set up progress for the current job | |
$bytesToProcess = $file.Length | |
expectedTicksForJob = $bytesToProcess * $estimate.TicksPerByte | |
$job = Start-Job -ScriptBlock $task -ArgumentList $file -Name "Counting $($file.Name)" | |
$ticksTakenForJob = 0 | |
$percentCompleteTotal = 100 * $estimate.BytesProcessed * $estimate.TicksPerByte / $estimate.TotalTicksExpected | |
Write-Progress -Activity "Counting occurrences" -Status "Processing $($file.Name)" -Id 0 -PercentComplete ([math]::Round($percentCompleteTotal)) -SecondsRemaining $secondsRemaining ([timespan]::FromTicks($estimate.TotalTicksExpected * (100 - $percentCompleteTotal))) | |
while($job.State -ne [System.Management.Automation.JobState]::Running -and $ticksTakenForJob -lt $expectedTicksForJob){ | |
Start-Sleep -Milliseconds 100 | |
$ticksTakenForJob = ([datetime]::Now - $job.PSBeginTime).Ticks | |
$percentAdd = 100 * $ticksTakenForJob / $expectedTicksForJob | |
$secondsRemaining = [timespan]::FromTicks($estimate.TotalTicksExpected - $ticksTakenForJob).TotalSeconds | |
Write-Progress -Activity "Counting occurrences" -Status "Processing $($file.Name)" -Id 0 -PercentComplete ([math]::Round($percentCompleteTotal + $percentAdd)) -SecondsRemaining $secondsRemaining | |
Write-Progress -Activity "Job" -Id 1 -PercentComplete ([math]::Round($percentAdd)) -SecondsRemaining ([timespan]::FromTicks($expectedTicksForJob - $ticksTakenForJob).TotalSeconds) -ParentId 0 | |
} | |
# Wait for the job to finish | |
$job | Wait-Job -Timeout ($Script:Timeout + 0.05 * [timespan]::FromTicks($expectedTicksForJob).TotalSeconds) | |
Write-Progress -Activity "Job" -Id 1 -Completed | |
# Take the weighted sum of the time taken for the current job and the time taken for previous jobs (note: bytes for current job gets mathematically canceled by division) | |
$estimate.TicksPerByte = (($job.PSEndTime - $job.PSBeginTime).Ticks) / $totalBytes + $estimate.TicksPerByte * ($estimate.BytesProcessed / $totalBytes) | |
$estimate.BytesProcessed += $file.Length | |
$estimate.TotalTicksExpected = $totalBytes * $estimate.TicksPerByte | |
} | |
$end = [datetime]::Now | |
# Then use that to calculate the time remaining for the entire list |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment