Get-PSOneToken.ps1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 |
function Get-PSOneToken { <# .SYNOPSIS Parses a PowerShell Script (*.ps1, *.psm1, *.psd1) and returns the token .DESCRIPTION Invokes the advanced PowerShell Parser and returns tokens and syntax errors .EXAMPLE Get-PSOneToken -Path c:\test.ps1 Parses the content of c:\test.ps1 and returns tokens and syntax errors .EXAMPLE Get-ChildItem -Path $home -Recurse -Include *.ps1,*.psm1,*.psd1 -File | Get-PSOneToken | Out-GridView parses all PowerShell files found anywhere in your user profile .EXAMPLE Get-ChildItem -Path $home -Recurse -Include *.ps1,*.psm1,*.psd1 -File | Get-PSOneToken | Where-Object Errors parses all PowerShell files found anywhere in your user profile and returns only those files that contain syntax errors .LINK https://powershell.one/powershell-internals/parsing-and-tokenization/advanced-tokenizer https://github.com/TobiasPSP/Modules.PSOneTools/blob/master/PSOneTools/1.4/Get-PSOneToken.ps1 #> [CmdletBinding(DefaultParameterSetName='Path')] param ( # Path to PowerShell script file # can be a string or any object that has a "Path" # or "FullName" property: [String] [Parameter(Mandatory,ValueFromPipeline,ParameterSetName='Path')] [Alias('FullName')] $Path, # PowerShell Code as ScriptBlock [ScriptBlock] [Parameter(Mandatory,ValueFromPipeline,ParameterSetName='ScriptBlock')] $ScriptBlock, # PowerShell Code as String [String] [Parameter(Mandatory, ValueFromPipeline,ParameterSetName='Code')] $Code, # the kind of token requested. If neither TokenKind nor TokenFlag is requested, # a full tokenization occurs [System.Management.Automation.Language.TokenKind[]] $TokenKind = $null, # the kind of token requested. If neither TokenKind nor TokenFlag is requested, # a full tokenization occurs [System.Management.Automation.Language.TokenFlags[]] $TokenFlag = $null, # include nested token that are contained inside # ExpandableString tokens [Switch] $IncludeNestedToken ) begin { # create variables to receive tokens and syntax errors: $errors = $tokens = $null # return tokens only? # when the user submits either one of these parameters, the return value should # be tokens of these kinds: $returnTokens = ($PSBoundParameters.ContainsKey('TokenKind')) -or ($PSBoundParameters.ContainsKey('TokenFlag')) } process { # if a scriptblock was submitted, convert it to string if ($PSCmdlet.ParameterSetName -eq 'ScriptBlock') { $Code = $ScriptBlock.ToString() } # if a path was submitted, read code from file, if ($PSCmdlet.ParameterSetName -eq 'Path') { $code = Get-Content -Path $Path -Raw -Encoding Default $name = Split-Path -Path $Path -Leaf $filepath = $Path # parse the file: $ast = [System.Management.Automation.Language.Parser]::ParseFile( $Path, [ref] $tokens, [ref]$errors) } else { # else the code is already present in $Code $name = $Code $filepath = '' # parse the string code: $ast = [System.Management.Automation.Language.Parser]::ParseInput( $Code, [ref] $tokens, [ref]$errors) } if ($IncludeNestedToken) { # "unwrap" nested token $tokens = $tokens | Expand-PSOneToken } if ($returnTokens) { # filter token and use fast scriptblock filtering instead of Where-Object: $tokens | & { process { if ($TokenKind -eq $null -or $TokenKind -contains $_.Kind) { $_ } }} | & { process { $token = $_ if ($TokenFlag -eq $null) { $token } else { $TokenFlag | Foreach-Object { if ($token.TokenFlags.HasFlag($_)) { $token } } | Select-Object -First 1 } } } } else { # return the results as a custom object [PSCustomObject]@{ Name = $name Path = $filepath Tokens = $tokens # "move" nested "Extent" up one level # so all important properties are shown immediately Errors = $errors | Select-Object -Property Message, IncompleteInput, ErrorId -ExpandProperty Extent Ast = $ast } } } } |