6 minute read

After Out-Gridview, the Show-Command seemed a logical next candidate. This provided me the oppertunity to tip-toe into Terminal User Interface (TUI) design.

This turned out to be a lot harder then I anticipated! So a full replacement of the Show-Command will most likely become multi-article posts. This one limits itself to scaffolding and a pseudo interface.

Command wrapping series

This article is part of a series on command wrapping for Linux. The goal is to expand the list of common cmdlets being supported on the Linux platform. Inspired by a call to action from Evgenij Smirnov during the 2025 European PowerShell Summit

The added value of Show-Command

Personally I do not use the Show-Command feature. Nor the integrated version of it in the classic PowerShell ISE. Especially in the terminal/command-line realm, the Get-Help function makes more sense to me.

If anything, it would make more sense to create a simple TUI replacement for the PowerShell ISE. In that case, there might be added value for cmdlet information. Still, on the Linux platform, there already are several fine command-line editors like Vim or Nano.

And Microsoft seems to have resurrected Microsoft Edit, programmed in Rust. The easy install under Windows 11 is;

winget install Microsoft.Edit

It is simple and intuitive. Would do exactly what I imagined my initial terminal version of PowerShell ISE would have done.

Or check out YouTube on Microsoft Edit

I embarked on this journey mainly because I wanted to get the PowerShell.Utility.Linux completed and secondly I wanted to learn a bit about TUI programming.

Show-Command

The Show-Command cmdlet presents a GUI in which you can search for PowerShell cmdlets. The GUI presents you the option to filter based on module but also search based on a partial name. Lastly, a ordered list of every cmdlet, complying with those search or filter parameters, is being shown.

Whenever a cmdlet is chosen, the GUI changes its interface to help you out with refining the actual command

A longer explanation is documented here: https://learn.microsoft.com/powershell/module/microsoft.powershell.utility/show-command

At time of this writing, on my machine the Show-Command cmdlet within PowerShell 7 seems to contain a bug. When I choose a command in the GUI, the following error appears:

Show-Command: Object reference not set to an instance of an object.

The interface then never changes to the second stage where you can refine your actual command parameters. All examples and references have been done with the PS5 version in order to move forward.

The next sections try to get the actual functionality working.

List the commands

This is the first stage of Show-Command; a list of all commands it can find. This one is simple:

$commands = Get-Command
$commands | Select-Object -Property Name

The modules selector

On the top of the User Interface, there is a ListView called ‘Modules’, which shows the modules where commands are from.

#all
$commands.Source | Select-Object -Unique

#single
$commands | Where-Object -Property Source -eq $selectedModule | Select-Object -Unique

The name filter

The second line on the interface, called ‘Name’, seems to be a text field in which you can enter a partial command name. This will filter the shown commands in the commands ListView below.

#all
$commands.Source | Select -unique

#single
$filteredCommand = $commands | Where-Object {$_.Name -like $partialName}
$filteredCommand | Select-Object -Property Name

Get the parameter sets

When a command is selected, the Show-Command User Interface changes. It displays the parameters details per parameters set. Secondly it displays the option to set common parameters. Let’s start with detecting the parameter sets

Is stumbled upon a blog from Patrick Gruenauer which interrogates Get-Command results.

I am using Get-Content as an example below because it has (only) two Parameter Sets.

$commands.where{$_.name -eq 'Get-Content'}|fl

Name             : Get-Content
CommandType      : Cmdlet
Definition       :
                   Get-Content [-Path] <string[]> [-ReadCount <long>] [-TotalCount <long>] [-Tail <int>] [-Filter <string>]
                   [-Include <string[]>] [-Exclude <string[]>] [-Force] [-Credential <pscredential>] [-UseTransaction] [-Delimiter
                   <string>] [-Wait] [-Raw] [-Encoding <FileSystemCmdletProviderEncoding>] [-Stream <string>] [<CommonParameters>]

                   Get-Content -LiteralPath <string[]> [-ReadCount <long>] [-TotalCount <long>] [-Tail <int>] [-Filter <string>]
                   [-Include <string[]>] [-Exclude <string[]>] [-Force] [-Credential <pscredential>] [-UseTransaction] [-Delimiter
                   <string>] [-Wait] [-Raw] [-Encoding <FileSystemCmdletProviderEncoding>] [-Stream <string>] [<CommonParameters>]

Path             :
AssemblyInfo     :
DLL              : C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\Microsoft.PowerShell.Commands.Management\v4.0_3.0.0.0__31bf3856ad364e3
                   5\Microsoft.PowerShell.Commands.Management.dll
HelpFile         : Microsoft.PowerShell.Commands.Management.dll-Help.xml
ParameterSets    : {[-Path] <string[]> [-ReadCount <long>] [-TotalCount <long>] [-Tail <int>] [-Filter <string>] [-Include
                   <string[]>] [-Exclude <string[]>] [-Force] [-Credential <pscredential>] [-UseTransaction] [-Delimiter <string>]
                   [-Wait] [-Raw] [-Encoding <FileSystemCmdletProviderEncoding>] [-Stream <string>] [<CommonParameters>],
                   -LiteralPath <string[]> [-ReadCount <long>] [-TotalCount <long>] [-Tail <int>] [-Filter <string>] [-Include
                   <string[]>] [-Exclude <string[]>] [-Force] [-Credential <pscredential>] [-UseTransaction] [-Delimiter <string>]
                   [-Wait] [-Raw] [-Encoding <FileSystemCmdletProviderEncoding>] [-Stream <string>] [<CommonParameters>]}
ImplementingType : Microsoft.PowerShell.Commands.GetContentCommand
Verb             : Get
Noun             : Content

$parameterSets = $commands.where{$_.name -eq 'Get-Content'}.ParameterSets
$parameterSets.Name
Path
LiteralPath

Further disquised is another pointer to which parameter set a parameter belongs to:

$parameterSets[0].Parameters.Attributes.ParameterSetName
__AllParameterSets
__AllParameterSets
__AllParameterSets
Path
__AllParameterSets
__AllParameterSets
__AllParameterSets
__AllParameterSets
__AllParameterSets
__AllParameterSets
__AllParameterSets
__AllParameterSets
__AllParameterSets
__AllParameterSets
__AllParameterSets
__AllParameterSets
__AllParameterSets
__AllParameterSets
__AllParameterSets
__AllParameterSets
__AllParameterSets
__AllParameterSets
__AllParameterSets
__AllParameterSets
__AllParameterSets
__AllParameterSets

Get the parameters from a parameter set

Once the parameter set is filtered, it becomes easy which parameters belong to that set:

$parameterSets[0].Parameters.Name
ReadCount
TotalCount
Tail
Path
Filter
Include
Exclude
Force
Credential
Verbose
Debug
ErrorAction
WarningAction
InformationAction
ErrorVariable
WarningVariable
InformationVariable
OutVariable
OutBuffer
PipelineVariable
UseTransaction
Delimiter
Wait
Raw
Encoding
Stream

Get the common parameters

I have found no clear .NET object or enumerator which lists the PowerShell common parameters. So the easiest way is to create our own:

enum commonParameters{
    Debug
    ErrorAction
    ErrorVariable
    InformationAction    
    InformationVariable
    OutVariable
    OutBuffer
    PipelineVariable
    ProgressAction
    Verbose
    WarningAction
    WarningVariable
}

The above will also help isolating the actual parameters for a given cmdlet, like so:

$parameterSets[0].Parameters.where{$_.Name -notin [commonParameters].GetEnumNames()}.Name
ReadCount
TotalCount
Tail
Path
Filter
Include
Exclude
Force
Credential
UseTransaction
Delimiter
Wait
Raw
Encoding
Stream

Get more parameter properties

In order to populate the different tabs which represent the parameter sets with actual parameter fields, we need to know what type of parameter we are dealing with.

$parameterSets[0].Parameters[0].ParameterType

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Int64                                    System.ValueType

Simple user interface

Below code is a very simple and basic. It should be viewed as a concept or flow scaffolding. At end, it is not a complete or working solution; it does not provide the option to enter values for parameters.

Import-Module Microsoft.PowerShell.ConsoleGuiTools

# Get available commands
$commands = Get-Command | Where-Object { $_.CommandType -eq 'Cmdlet' -or $_.CommandType -eq 'Function' }
$commandNames = $commands | Select-Object -ExpandProperty Name

# Select a command using Out-ConsoleGridView
$selectedCmd = $commandNames | Out-ConsoleGridView -Title 'Select a command to show parameters' -OutputMode Single
if (-not $selectedCmd) { return }

# Get parameter sets
$selectedCmd = Get-Command -Name $selectedCmd
$parameterSets = @()
$parameterSets += $selectedCmd.ParameterSets
if ($parameterSets.count -gt 1) {
    $selectedParameterSet = $parameterSets.Name | Out-ConsoleGridView -Title 'Select a Parameter Set' -OutputMode Single
    if (-not $selectedParameterSet) { return }
} else {
    $selectedParameterSet = $parameterSets[0].Name
}

# Strip the common parameters from the parameter set
enum commonParameters{
    Debug
    ErrorAction
    ErrorVariable
    InformationAction    
    InformationVariable
    OutVariable
    OutBuffer
    PipelineVariable
    ProgressAction
    Verbose
    WarningAction
    WarningVariable
}
$selectedParameterSet = $selectedCmd.ParameterSets | Where-Object { $_.Name -eq $selectedParameterSet }
$actualParameters = $selectedParameterSet.Parameters.Where{$_.Name -notin [commonParameters].GetEnumNames()}

# Get parameter info
$paramInfo = $actualParameters | ForEach-Object {
    [PSCustomObject]@{
        Name = $_.Name
        Type = $_.ParameterType.Name
        IsMandatory = $_.IsMandatory
        DefaultValue = $_.DefaultValue
    }
}

# Prepare input form for parameters
$formFields = $paramInfo | ForEach-Object {
    [PSCustomObject]@{
        Parameter = $_.Name
        Value = $_.DefaultValue
    }
}

# Show parameter input grid
$inputParams = $formFields | Out-ConsoleGridView -Title "Enter parameter values for $selectedCmd (edit Value column)" -OutputMode Multiple
if (-not $inputParams) { return }

# Build command line
$paramString = $inputParams | Where-Object { $_.Value -ne $null -and $_.Value -ne '' } | ForEach-Object {
    "-$_($._.Parameter) $_($._.Value)"
} | Join-String ' '

$finalCommand = "$selectedCmd $paramString"

# Show final command and offer to run
Write-Host "Command to run: $finalCommand" -ForegroundColor Cyan
$run = Read-Host 'Run this command? (y/n)'
if ($run -eq 'y') {
    Invoke-Expression $finalCommand
}

Updated: