Home > Net >  Count commandlet occurence in powershell script at different points
Count commandlet occurence in powershell script at different points

Time:09-16

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 these if 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)
  • Related