completers/wevtutil_completer/wevtutil_completer.ps1
|
# wevtutil tab completion for PowerShell # Provides static and dynamic completion for wevtutil commands, options, and key values. Set-StrictMode -Version 2.0 if (-not (Get-Variable -Name WevtutilCompletionCatalog -Scope Script -ErrorAction Ignore)) { $script:WevtutilCompletionCatalog = @{ Initialized = $false CommandAliases = @{} CommandSuggestions = @() OptionsByCommand = @{} ValueHintsByCommand = @{} PathOptionsByCommand = @{} PlaceholdersByCommand = @{} LogNamesCache = @() LogNamesCacheUpdated = $null PublisherNamesCache = @() PublisherNamesCacheUpdated = $null } } function New-WevtutilCompletionResult { param( [string]$CompletionText, [string]$ResultType, [string]$ToolTip ) if ([string]::IsNullOrWhiteSpace($ToolTip)) { $ToolTip = $CompletionText } [System.Management.Automation.CompletionResult]::new( $CompletionText, $CompletionText, $ResultType, $ToolTip ) } function ConvertTo-WevtutilQuotedValue { param( [string]$Value, [bool]$AlwaysQuote = $false ) if ([string]::IsNullOrWhiteSpace($Value)) { return $Value } if (($AlwaysQuote -or $Value -match '\s') -and -not ($Value.StartsWith('"') -and $Value.EndsWith('"'))) { return '"' + $Value + '"' } $Value } function Get-WevtutilCurrentToken { param( [string]$Line, [int]$CursorPosition, [string]$Fallback ) if ([string]::IsNullOrWhiteSpace($Line)) { return $Fallback } $safeCursor = [Math]::Min([Math]::Max($CursorPosition, 0), $Line.Length) $prefix = $Line.Substring(0, $safeCursor) if ($prefix -match '\s$') { return '' } $parts = @([regex]::Matches($prefix, '"[^"]*"|\S+') | ForEach-Object { $_.Value }) if ($parts.Count -gt 0) { return $parts[-1] } $Fallback } function Test-WevtutilCommandAvailable { [bool](Get-Command -Name wevtutil.exe -ErrorAction SilentlyContinue) } function Initialize-WevtutilCompletionCatalog { if ($script:WevtutilCompletionCatalog.Initialized) { return } $remoteOptions = @('/r:', '/remote:', '/u:', '/username:', '/p:', '/password:', '/a:', '/authentication:') $unicodeOptions = @('/uni:', '/unicode:') $commonOptions = @($remoteOptions + $unicodeOptions) $commonWithHelp = @($commonOptions + '/?') $unicodeWithHelp = @($unicodeOptions + '/?') $commands = @( @{ Canonical = 'el'; Description = 'List log names.'; Suggestions = @('el', 'enum-logs') }, @{ Canonical = 'gl'; Description = 'Get log configuration information.'; Suggestions = @('gl', 'get-log') }, @{ Canonical = 'sl'; Description = 'Modify configuration of a log.'; Suggestions = @('sl', 'set-log') }, @{ Canonical = 'ep'; Description = 'List event publishers.'; Suggestions = @('ep', 'enum-publishers') }, @{ Canonical = 'gp'; Description = 'Get configuration information for event publishers.'; Suggestions = @('gp', 'get-publisher') }, @{ Canonical = 'im'; Description = 'Install event publishers and logs from a manifest.'; Suggestions = @('im', 'install-manifest') }, @{ Canonical = 'um'; Description = 'Uninstall event publishers and logs from a manifest.'; Suggestions = @('um', 'uninstall-manifest') }, @{ Canonical = 'qe'; Description = 'Query events from a log, log file, or structured query.'; Suggestions = @('qe', 'query-events') }, @{ Canonical = 'gli'; Description = 'Get log status information.'; Suggestions = @('gli', 'get-loginfo') }, @{ Canonical = 'epl'; Description = 'Export events from a log.'; Suggestions = @('epl', 'export-log') }, @{ Canonical = 'al'; Description = 'Archive a log file in a self-contained format.'; Suggestions = @('al', 'archive-log') }, @{ Canonical = 'cl'; Description = 'Clear a log and optionally back it up.'; Suggestions = @('cl', 'clear-log') } ) $commandAliases = @{} $commandSuggestions = New-Object System.Collections.Generic.List[object] foreach ($command in $commands) { foreach ($suggestion in $command.Suggestions) { $commandAliases[$suggestion.ToLowerInvariant()] = $command.Canonical $commandSuggestions.Add([pscustomobject]@{ CompletionText = $suggestion ToolTip = $command.Description }) } } $script:WevtutilCompletionCatalog.CommandAliases = $commandAliases $script:WevtutilCompletionCatalog.CommandSuggestions = @($commandSuggestions.ToArray()) $script:WevtutilCompletionCatalog.OptionsByCommand = @{ 'el' = @($commonWithHelp) 'gl' = @('/f:', '/format:') + $commonWithHelp 'sl' = @('/e:', '/enabled:', '/q:', '/quiet:', '/fm:', '/filemax:', '/i:', '/isolation:', '/lfn:', '/logfilename:', '/rt:', '/retention:', '/ab:', '/autobackup:', '/ms:', '/maxsize:', '/l:', '/level:', '/k:', '/keywords:', '/ca:', '/channelaccess:', '/c:', '/config:') + $commonWithHelp 'ep' = @($commonWithHelp) 'gp' = @('/ge:', '/getevents:', '/gm:', '/getmessage:', '/f:', '/format:') + $commonWithHelp 'im' = @('/rf:', '/resourceFilePath:', '/mf:', '/messageFilePath:', '/pf:', '/parameterFilePath:') + $unicodeWithHelp 'um' = @($unicodeWithHelp) 'qe' = @('/lf:', '/logfile:', '/sq:', '/structuredquery:', '/q:', '/query:', '/bm:', '/bookmark:', '/sbm:', '/savebookmark:', '/rd:', '/reversedirection:', '/f:', '/format:', '/l:', '/locale:', '/c:', '/count:', '/e:', '/element:') + $commonWithHelp 'gli' = @('/lf:', '/logfile:') + $commonWithHelp 'epl' = @('/lf:', '/logfile:', '/sq:', '/structuredquery:', '/q:', '/query:', '/ow:', '/overwrite:') + $commonWithHelp 'al' = @('/l:', '/locale:') + $commonWithHelp 'cl' = @('/bu:', '/backup:') + $commonWithHelp } $script:WevtutilCompletionCatalog.ValueHintsByCommand = @{ '__common__' = @{ '/a:' = @('Default', 'Negotiate', 'Kerberos', 'NTLM') '/authentication:' = @('Default', 'Negotiate', 'Kerberos', 'NTLM') '/uni:' = @('true', 'false') '/unicode:' = @('true', 'false') } 'gl' = @{ '/f:' = @('XML', 'Text') '/format:' = @('XML', 'Text') } 'sl' = @{ '/e:' = @('true', 'false') '/enabled:' = @('true', 'false') '/q:' = @('true', 'false') '/quiet:' = @('true', 'false') '/i:' = @('system', 'application', 'custom') '/isolation:' = @('system', 'application', 'custom') '/rt:' = @('true', 'false') '/retention:' = @('true', 'false') '/ab:' = @('true', 'false') '/autobackup:' = @('true', 'false') '/l:' = @('0', '1', '2', '3', '4', '5') '/level:' = @('0', '1', '2', '3', '4', '5') } 'gp' = @{ '/ge:' = @('true', 'false') '/getevents:' = @('true', 'false') '/gm:' = @('true', 'false') '/getmessage:' = @('true', 'false') '/f:' = @('XML', 'Text') '/format:' = @('XML', 'Text') } 'qe' = @{ '/lf:' = @('true', 'false') '/logfile:' = @('true', 'false') '/sq:' = @('true', 'false') '/structuredquery:' = @('true', 'false') '/rd:' = @('true', 'false') '/reversedirection:' = @('true', 'false') '/f:' = @('XML', 'Text', 'RenderedXml') '/format:' = @('XML', 'Text', 'RenderedXml') } 'gli' = @{ '/lf:' = @('true', 'false') '/logfile:' = @('true', 'false') } 'epl' = @{ '/lf:' = @('true', 'false') '/logfile:' = @('true', 'false') '/sq:' = @('true', 'false') '/structuredquery:' = @('true', 'false') '/ow:' = @('true', 'false') '/overwrite:' = @('true', 'false') } } # Path-taking options are keyed per command: the same prefix means different things on # different verbs (sl /c: is a config file, qe /c: is an event count). $script:WevtutilCompletionCatalog.PathOptionsByCommand = @{ 'sl' = @{ '/c:' = @('.xml') '/config:' = @('.xml') '/lfn:' = @('.etl', '.evt', '.evtx', '.log') '/logfilename:' = @('.etl', '.evt', '.evtx', '.log') } 'im' = @{ '/rf:' = @() '/resourcefilepath:' = @() '/mf:' = @() '/messagefilepath:' = @() '/pf:' = @() '/parameterfilepath:' = @() } 'qe' = @{ '/bm:' = @('.xml') '/bookmark:' = @('.xml') '/sbm:' = @('.xml') '/savebookmark:' = @('.xml') } 'cl' = @{ '/bu:' = @('.evtx') '/backup:' = @('.evtx') } } # Value-bearing options with neither an enum nor a path get a placeholder so the slot # is visible and the option name is never echoed back as a ParameterName. $script:WevtutilCompletionCatalog.PlaceholdersByCommand = @{ '__common__' = @{ '/r:' = '<computer>' '/remote:' = '<computer>' '/u:' = '<username>' '/username:' = '<username>' '/p:' = '<password>' '/password:' = '<password>' } 'sl' = @{ '/fm:' = '<n>' '/filemax:' = '<n>' '/ms:' = '<n>' '/maxsize:' = '<n>' '/k:' = '<keyword-mask>' '/keywords:' = '<keyword-mask>' '/ca:' = '<sddl>' '/channelaccess:' = '<sddl>' } 'qe' = @{ '/q:' = '<xpath>' '/query:' = '<xpath>' '/l:' = '<locale>' '/locale:' = '<locale>' '/c:' = '<n>' '/count:' = '<n>' '/e:' = '<root-element>' '/element:' = '<root-element>' } 'epl' = @{ '/q:' = '<xpath>' '/query:' = '<xpath>' } 'al' = @{ '/l:' = '<locale>' '/locale:' = '<locale>' } } $script:WevtutilCompletionCatalog.Initialized = $true } function Resolve-WevtutilCommandAlias { param([string]$Token) if ([string]::IsNullOrWhiteSpace($Token)) { return $null } $key = $Token.ToLowerInvariant() if ($script:WevtutilCompletionCatalog.CommandAliases.ContainsKey($key)) { return $script:WevtutilCompletionCatalog.CommandAliases[$key] } $null } function Get-WevtutilActiveCommand { param([string[]]$Tokens) foreach ($token in $Tokens) { $resolved = Resolve-WevtutilCommandAlias -Token $token if ($resolved) { return $resolved } } $null } function Get-WevtutilTokensAfterCommand { param([string[]]$Tokens) for ($i = 0; $i -lt $Tokens.Count; $i++) { if (Resolve-WevtutilCommandAlias -Token $Tokens[$i]) { if ($i -lt ($Tokens.Count - 1)) { return @($Tokens[($i + 1)..($Tokens.Count - 1)]) } return @() } } @() } function Get-WevtutilOptionPrefixFromToken { param([string]$Token) if ([string]::IsNullOrWhiteSpace($Token)) { return $null } if ($Token -match '^(/[A-Za-z][A-Za-z0-9\-]*:)') { return $matches[1] } $null } function Get-WevtutilValueHints { param( [string]$Command, [string]$OptionPrefix ) if ([string]::IsNullOrWhiteSpace($OptionPrefix)) { return @() } $optionKey = $OptionPrefix.ToLowerInvariant() $values = @() if ($script:WevtutilCompletionCatalog.ValueHintsByCommand.ContainsKey('__common__')) { $commonMap = $script:WevtutilCompletionCatalog.ValueHintsByCommand['__common__'] if ($commonMap.ContainsKey($optionKey)) { $values += $commonMap[$optionKey] } } if ($Command -and $script:WevtutilCompletionCatalog.ValueHintsByCommand.ContainsKey($Command)) { $commandMap = $script:WevtutilCompletionCatalog.ValueHintsByCommand[$Command] if ($commandMap.ContainsKey($optionKey)) { $values += $commandMap[$optionKey] } } @($values | Sort-Object -Unique) } function Test-WevtutilPathLikeOption { param( [string]$Command, [string]$OptionPrefix ) if ([string]::IsNullOrWhiteSpace($Command) -or [string]::IsNullOrWhiteSpace($OptionPrefix)) { return $false } $pathOptions = $script:WevtutilCompletionCatalog.PathOptionsByCommand $pathOptions.ContainsKey($Command) -and $pathOptions[$Command].ContainsKey($OptionPrefix.ToLowerInvariant()) } function Get-WevtutilAllowedExtensionsForOption { param( [string]$Command, [string]$OptionPrefix ) if (-not (Test-WevtutilPathLikeOption -Command $Command -OptionPrefix $OptionPrefix)) { return @() } @($script:WevtutilCompletionCatalog.PathOptionsByCommand[$Command][$OptionPrefix.ToLowerInvariant()]) } function Get-WevtutilValuePlaceholder { param( [string]$Command, [string]$OptionPrefix ) if ([string]::IsNullOrWhiteSpace($OptionPrefix)) { return $null } $optionKey = $OptionPrefix.ToLowerInvariant() $placeholders = $script:WevtutilCompletionCatalog.PlaceholdersByCommand foreach ($bucket in @($Command, '__common__')) { if ($bucket -and $placeholders.ContainsKey($bucket) -and $placeholders[$bucket].ContainsKey($optionKey)) { return $placeholders[$bucket][$optionKey] } } $null } function Get-WevtutilPathCompletions { param( [string]$InputPath, [string[]]$AllowedExtensions ) $cleanInput = if ([string]::IsNullOrWhiteSpace($InputPath)) { '' } else { $InputPath.Trim('"') } # Split on the last separator explicitly: a trailing '\' means "list this directory" # (Split-Path -Leaf would hand back the directory's own name), and the typed parent is # re-attached verbatim so a relative path is not rewritten to an absolute one. $typedParent = '' $leaf = $cleanInput $separatorIndex = $cleanInput.LastIndexOfAny([char[]]@('\', '/')) if ($separatorIndex -ge 0) { $typedParent = $cleanInput.Substring(0, $separatorIndex + 1) $leaf = $cleanInput.Substring($separatorIndex + 1) } $enumeratePath = if ([string]::IsNullOrEmpty($typedParent)) { '.' } else { $typedParent } $filter = if ([string]::IsNullOrWhiteSpace($leaf)) { '*' } else { "$leaf*" } $alwaysQuote = -not [string]::IsNullOrEmpty($InputPath) -and $InputPath.StartsWith('"') $items = @(Get-ChildItem -LiteralPath $enumeratePath -Filter $filter -ErrorAction Ignore) if ($AllowedExtensions -and $AllowedExtensions.Count -gt 0) { $items = @($items | Where-Object { $_.PSIsContainer -or ($AllowedExtensions -contains $_.Extension.ToLowerInvariant()) }) } foreach ($item in $items) { ConvertTo-WevtutilQuotedValue -Value ($typedParent + $item.Name) -AlwaysQuote $alwaysQuote } } function Test-WevtutilOptionPresent { param( [string[]]$Tokens, [string[]]$OptionNames ) if (-not $Tokens -or -not $OptionNames) { return $false } $lookup = @{} foreach ($name in $OptionNames) { $lookup[$name.ToLowerInvariant()] = $true } foreach ($token in $Tokens) { if ($token -match '^(/[^:]+)(?::.*)?$') { $optionName = $matches[1].ToLowerInvariant() if ($lookup.ContainsKey($optionName)) { return $true } } } $false } function Get-WevtutilOptionValue { param( [string[]]$Tokens, [string[]]$OptionNames ) if (-not $Tokens -or -not $OptionNames) { return $null } $lookup = @{} foreach ($name in $OptionNames) { $lookup[$name.ToLowerInvariant()] = $true } for ($i = $Tokens.Count - 1; $i -ge 0; $i--) { $token = $Tokens[$i] if ($token -match '^(/[^:]+):(.*)$') { $optionName = $matches[1].ToLowerInvariant() if ($lookup.ContainsKey($optionName)) { return $matches[2] } } } $null } function Test-WevtutilOptionTrue { param( [string[]]$Tokens, [string[]]$OptionNames ) $value = Get-WevtutilOptionValue -Tokens $Tokens -OptionNames $OptionNames if ($null -eq $value) { return $false } $value.Equals('true', [System.StringComparison]::OrdinalIgnoreCase) } function Get-WevtutilPositionalArguments { param([string[]]$Tokens) @($Tokens | Where-Object { $_ -and -not $_.StartsWith('/') }) } function Get-WevtutilExpectedPositionalKind { param( [string]$Command, [string[]]$TokensAfterCommand ) if ([string]::IsNullOrWhiteSpace($Command)) { return $null } $positionalCount = @(Get-WevtutilPositionalArguments -Tokens $TokensAfterCommand).Count $usesConfig = Test-WevtutilOptionPresent -Tokens $TokensAfterCommand -OptionNames @('/c', '/config') $usesLogFile = Test-WevtutilOptionTrue -Tokens $TokensAfterCommand -OptionNames @('/lf', '/logfile') $usesStructuredQuery = Test-WevtutilOptionTrue -Tokens $TokensAfterCommand -OptionNames @('/sq', '/structuredquery') switch ($Command) { 'gl' { if ($positionalCount -eq 0) { return 'logname' } } 'sl' { if (-not $usesConfig -and $positionalCount -eq 0) { return 'logname' } } 'gp' { if ($positionalCount -eq 0) { return 'publisher' } } 'im' { if ($positionalCount -eq 0) { return 'manifest' } } 'um' { if ($positionalCount -eq 0) { return 'manifest' } } 'qe' { if ($positionalCount -eq 0) { if ($usesStructuredQuery) { return 'structuredquery' } if ($usesLogFile) { return 'logfile' } return 'logname' } } 'gli' { if ($positionalCount -eq 0) { if ($usesLogFile) { return 'logfile' } return 'logname' } } 'epl' { if ($positionalCount -eq 0) { if ($usesStructuredQuery) { return 'structuredquery' } if ($usesLogFile) { return 'logfile' } return 'logname' } if ($positionalCount -eq 1) { return 'targetfile' } } 'al' { if ($positionalCount -eq 0) { return 'logfile' } } 'cl' { if ($positionalCount -eq 0) { return 'logname' } } } $null } function Get-WevtutilAllowedExtensionsForPositionalKind { param([string]$Kind) switch ($Kind) { 'manifest' { return @('.man', '.xml') } 'logfile' { return @('.etl', '.evt', '.evtx') } 'structuredquery' { return @('.xml') } 'targetfile' { return @('.evtx') } default { return @() } } } function Update-WevtutilLogNameCache { $lastUpdated = $script:WevtutilCompletionCatalog.LogNamesCacheUpdated if ($null -ne $lastUpdated) { $cacheAge = (Get-Date) - $lastUpdated if ($cacheAge.TotalSeconds -lt 120 -and $script:WevtutilCompletionCatalog.LogNamesCache.Count -gt 0) { return } } if (-not (Test-WevtutilCommandAvailable)) { $script:WevtutilCompletionCatalog.LogNamesCache = @() $script:WevtutilCompletionCatalog.LogNamesCacheUpdated = Get-Date return } try { $logNames = @(& wevtutil.exe el 2>$null | Where-Object { -not [string]::IsNullOrWhiteSpace($_) }) $script:WevtutilCompletionCatalog.LogNamesCache = @($logNames | Sort-Object -Unique) } catch { $script:WevtutilCompletionCatalog.LogNamesCache = @() } $script:WevtutilCompletionCatalog.LogNamesCacheUpdated = Get-Date } function Get-WevtutilLogNameCompletions { param([string]$WordToComplete) Update-WevtutilLogNameCache $cleanPrefix = $WordToComplete.Trim('"') $alwaysQuote = $WordToComplete.StartsWith('"') $script:WevtutilCompletionCatalog.LogNamesCache | Where-Object { $_ -like ([System.Management.Automation.WildcardPattern]::Escape($cleanPrefix) + '*') } | ForEach-Object { ConvertTo-WevtutilQuotedValue -Value $_ -AlwaysQuote $alwaysQuote } } function Update-WevtutilPublisherNameCache { $lastUpdated = $script:WevtutilCompletionCatalog.PublisherNamesCacheUpdated if ($null -ne $lastUpdated) { $cacheAge = (Get-Date) - $lastUpdated if ($cacheAge.TotalSeconds -lt 120 -and $script:WevtutilCompletionCatalog.PublisherNamesCache.Count -gt 0) { return } } if (-not (Test-WevtutilCommandAvailable)) { $script:WevtutilCompletionCatalog.PublisherNamesCache = @() $script:WevtutilCompletionCatalog.PublisherNamesCacheUpdated = Get-Date return } try { $publishers = @(& wevtutil.exe ep 2>$null | Where-Object { -not [string]::IsNullOrWhiteSpace($_) }) $script:WevtutilCompletionCatalog.PublisherNamesCache = @($publishers | Sort-Object -Unique) } catch { $script:WevtutilCompletionCatalog.PublisherNamesCache = @() } $script:WevtutilCompletionCatalog.PublisherNamesCacheUpdated = Get-Date } function Get-WevtutilPublisherNameCompletions { param([string]$WordToComplete) Update-WevtutilPublisherNameCache $cleanPrefix = $WordToComplete.Trim('"') $alwaysQuote = $WordToComplete.StartsWith('"') $script:WevtutilCompletionCatalog.PublisherNamesCache | Where-Object { $_ -like ([System.Management.Automation.WildcardPattern]::Escape($cleanPrefix) + '*') } | ForEach-Object { ConvertTo-WevtutilQuotedValue -Value $_ -AlwaysQuote $alwaysQuote } } function Complete-Wevtutil { param( [string]$wordToComplete, [System.Management.Automation.Language.CommandAst]$commandAst, [int]$cursorPosition ) Initialize-WevtutilCompletionCatalog $allTokens = @($commandAst.CommandElements | ForEach-Object { $_.Extent.Text }) $tokens = @($allTokens | Select-Object -Skip 1) $line = $commandAst.ToString() $hasTrailingSpace = ([string]::IsNullOrEmpty($wordToComplete) -and $cursorPosition -ge $line.Length) -or ($line -match '\s$') $currentWord = if ($hasTrailingSpace) { '' } elseif ([string]::IsNullOrWhiteSpace($wordToComplete)) { Get-WevtutilCurrentToken -Line $line -CursorPosition ($cursorPosition - $commandAst.Extent.StartOffset) -Fallback $wordToComplete } else { $wordToComplete } if ($hasTrailingSpace) { $tokensBeforeCurrent = @($tokens) } elseif ($tokens.Count -gt 1) { $tokensBeforeCurrent = @($tokens | Select-Object -First ($tokens.Count - 1)) } else { $tokensBeforeCurrent = @() } $activeCommand = Get-WevtutilActiveCommand -Tokens $tokensBeforeCurrent $tokensAfterCommand = Get-WevtutilTokensAfterCommand -Tokens $tokensBeforeCurrent $currentPrefix = Get-WevtutilOptionPrefixFromToken -Token $currentWord if ($currentPrefix) { $typedValue = $currentWord.Substring($currentPrefix.Length) $valueHints = Get-WevtutilValueHints -Command $activeCommand -OptionPrefix $currentPrefix if (@($valueHints).Count -gt 0) { return $valueHints | Where-Object { $_ -like ([System.Management.Automation.WildcardPattern]::Escape($typedValue) + '*') } | ForEach-Object { New-WevtutilCompletionResult -CompletionText "$currentPrefix$_" -ResultType 'ParameterValue' -ToolTip $_ } } if (Test-WevtutilPathLikeOption -Command $activeCommand -OptionPrefix $currentPrefix) { $allowedExtensions = Get-WevtutilAllowedExtensionsForOption -Command $activeCommand -OptionPrefix $currentPrefix return Get-WevtutilPathCompletions -InputPath $typedValue -AllowedExtensions $allowedExtensions | ForEach-Object { New-WevtutilCompletionResult -CompletionText "$currentPrefix$_" -ResultType 'ParameterValue' -ToolTip $_ } } $placeholder = Get-WevtutilValuePlaceholder -Command $activeCommand -OptionPrefix $currentPrefix if ($placeholder) { $valueText = if ([string]::IsNullOrEmpty($typedValue)) { $placeholder } else { $typedValue } return @(New-WevtutilCompletionResult -CompletionText "$currentPrefix$valueText" -ResultType 'ParameterValue' -ToolTip "$currentPrefix$placeholder") } # The word is already an /option: value slot; never fall through to option-name # completion, which would only echo the option back as a ParameterName. return @() } if (-not $activeCommand) { if ([string]::IsNullOrWhiteSpace($currentWord) -or -not $currentWord.StartsWith('/')) { return $script:WevtutilCompletionCatalog.CommandSuggestions | Where-Object { $_.CompletionText -like ([System.Management.Automation.WildcardPattern]::Escape($currentWord) + '*') } | ForEach-Object { New-WevtutilCompletionResult -CompletionText $_.CompletionText -ResultType 'ParameterName' -ToolTip $_.ToolTip } } if ($currentWord.StartsWith('/')) { return @('/?') | Where-Object { $_ -like ([System.Management.Automation.WildcardPattern]::Escape($currentWord) + '*') } | ForEach-Object { New-WevtutilCompletionResult -CompletionText $_ -ResultType 'ParameterName' -ToolTip $_ } } return @() } $expectedPositionalKind = Get-WevtutilExpectedPositionalKind -Command $activeCommand -TokensAfterCommand $tokensAfterCommand if ($expectedPositionalKind -and -not $currentWord.StartsWith('/')) { switch ($expectedPositionalKind) { 'logname' { return Get-WevtutilLogNameCompletions -WordToComplete $currentWord | ForEach-Object { New-WevtutilCompletionResult -CompletionText $_ -ResultType 'ParameterValue' -ToolTip $_ } } 'publisher' { return Get-WevtutilPublisherNameCompletions -WordToComplete $currentWord | ForEach-Object { New-WevtutilCompletionResult -CompletionText $_ -ResultType 'ParameterValue' -ToolTip $_ } } default { $allowedExtensions = Get-WevtutilAllowedExtensionsForPositionalKind -Kind $expectedPositionalKind return Get-WevtutilPathCompletions -InputPath $currentWord -AllowedExtensions $allowedExtensions | ForEach-Object { New-WevtutilCompletionResult -CompletionText $_ -ResultType 'ParameterValue' -ToolTip $_ } } } } if ([string]::IsNullOrWhiteSpace($currentWord) -or $currentWord.StartsWith('/')) { $suggestions = @() if ($script:WevtutilCompletionCatalog.OptionsByCommand.ContainsKey($activeCommand)) { $suggestions = @($script:WevtutilCompletionCatalog.OptionsByCommand[$activeCommand]) } return $suggestions | Sort-Object -Unique | Where-Object { $_ -like ([System.Management.Automation.WildcardPattern]::Escape($currentWord) + '*') } | ForEach-Object { New-WevtutilCompletionResult -CompletionText $_ -ResultType 'ParameterName' -ToolTip $_ } } @() } # Register the completer Register-ArgumentCompleter -Native -CommandName 'wevtutil', 'wevtutil.exe' -ScriptBlock { param($wordToComplete, $commandAst, $cursorPosition) Complete-Wevtutil -wordToComplete $wordToComplete -commandAst $commandAst -cursorPosition $cursorPosition } |