I found some great sample code that autocounts the number of times I call the progress bar cmdlet in a script. Super awesome, then you dont have to manually increment steps and change stuff all the time.
$steps = ([System.Management.Automation.PsParser]::Tokenize((gc "$PSScriptRoot\$($MyInvocation.MyCommand.Name)"), [ref]$null) | where { $_.Type -eq 'Command' -and $_.Content -eq 'Write-ProgressHelper' }).Count
However! I am building a giant script that has multiple sections within it, each time I reboot I start the script at a different point.
I would like to display the overall progress of the ENTIRE installation, so at the start of each step I need to know how many occurrences of Write-Progress
occurred before the section that current runs.
Ex: if I call the script to start at point 2, the progress bar should be starting at 4/7.
Is there a way to count the occurrences at a certain point in the script? The tokenize just collects the whole script.
Param(
[Parameter(Mandatory=$True)]
[int]
$startstep
#starting step for script, called with
)
function Write-ProgressHelper {
param(
[int]$StepNumber,
[string]$Message
)
Write-Progress -ID 0 -Activity 'Installation Part 1' -Status $Message -PercentComplete (($StepNumber / $steps) * 100)
#call in code with Write-ProgressHelper -Message 'Doing something' -StepNumber ($step )
}
$steps = ([System.Management.Automation.PsParser]::Tokenize((gc "$PSScriptRoot\$($MyInvocation.MyCommand.Name)"), [ref]$null) | where { $_.Type -eq 'Command' -and $_.Content -eq 'Write-ProgressHelper' }).Count
$step = 0
if ($startstep -eq 1){
#count the number of occurences here and set STEP to that value (4 in this case)
write-host "Step $startstep"
Write-ProgressHelper -Message 'part 1se' -StepNumber ($step )
sleep -Seconds 5
write-host "Step $startstep"
Write-ProgressHelper -Message 'part 1se' -StepNumber ($step )
sleep -Seconds 5
write-host "Step $startstep"
Write-ProgressHelper -Message 'part 1se' -StepNumber ($step )
sleep -Seconds 5
write-host "Step $startstep"
Write-ProgressHelper -Message 'part 1se' -StepNumber ($step )
sleep -Seconds 5
}
if ($startstep-eq 2){
write-host "Step $startstep"
Write-ProgressHelper -Message 'Part 2' -StepNumber ($step )
sleep -Seconds 5
write-host "Step $startstep"
Write-ProgressHelper -Message 'part 1se' -StepNumber ($step )
sleep -Seconds 5
write-host "Step $startstep"
Write-ProgressHelper -Message 'part 1se' -StepNumber ($step )
sleep -Seconds 5
}
CodePudding user response:
This can be done using the AST (abstract syntax tree).
- Find the
if
statements that check for the right value of$startstep
. - Count the number of
Write-ProgressHelper
invocations within the bodies of theseif
statements.
# these variables must exist and will be filled by reference later:
$tokens = $errors = $null
# parse the current script
$ast = [Management.Automation.Language.Parser]::ParseFile( $MyInvocation.MyCommand.Path, [ref] $tokens, [ref] $errors)
# get total number of invocations of Write-ProgressHelper
$steps = $ast.FindAll({ param($item)
$item -is [Management.Automation.Language.CommandAst] -and $item.GetCommandName() -eq 'Write-ProgressHelper'
}, $true).Count
# get if-statements that check for less than current value of $startstep
$ifAsts = $ast.FindAll({ param($item)
if( $item -isnot [Management.Automation.Language.IfStatementAst] ) { return $false }
# Item1 contains the AST of the if statement condition
$item.Clauses.Item1.Extent.Text -match '\$startstep -eq (\d )' -and ([int] $matches[1]) -lt $startstep
}, $true)
# get number of invocations of Write-ProgressHelper within body of matching if-statements
$step = 0
$ifAsts | ForEach-Object {
# Item2 contains the AST of the if statement body
$step = $_.Clauses.Item2.FindAll({ param($item)
$item -is [Management.Automation.Language.CommandAst] -and $item.GetCommandName() -eq 'Write-ProgressHelper'
}, $true).Count
}
A good starting point for further experimentation is to list all items of the AST like this:
$ast.FindAll({ $true }, $true)