Better PowerShell Help (Part 2)

by May 13, 2021

In the previous tip we changed the default parameter values of Get-Help to automatically show the rich online help when you use Get-Help or the common parameter -?. However, when a cmdlet has no online help, this approach produces errors.

A better approach would first check whether there is online help for a given command, and only then open the online help. In case there is no online help, the default local help should show.

This approach cannot be implemented via default parameters. Instead, the Get-Help cmdlet itself needs adjustments. To add the logic to Get-Help, you can use this proxy function:

function Get-Help
{
  # clone the original param block taken from Get-Help
  [CmdletBinding(DefaultParameterSetName='AllUsersView', HelpUri='https://go.microsoft.com/fwlink/?LinkID=113316')]
  param(
    [Parameter(Position=0, ValueFromPipelineByPropertyName)]
    [string]
    $Name,

    [Parameter(ParameterSetName='Online', Mandatory)]
    [switch]
    $Online,

    [ValidateSet('Alias','Cmdlet','Provider','General','FAQ','Glossary','HelpFile','ScriptCommand','Function','Filter','ExternalScript','All','DefaultHelp','Workflow','DscResource','Class','Configuration')]
    [string[]]
    $Category,

    [string]
    $Path,

    [string[]]
    $Component,

    [string[]]
    $Functionality,

    [string[]]
    $Role,

    [Parameter(ParameterSetName='DetailedView', Mandatory)]
    [switch]
    $Detailed,

    [Parameter(ParameterSetName='AllUsersView')]
    [switch]
    $Full,

    [Parameter(ParameterSetName='Examples', Mandatory)]
    [switch]
    $Examples,

    [Parameter(ParameterSetName='Parameters', Mandatory)]
    [string]
    $Parameter,

    [Parameter(ParameterSetName='ShowWindow', Mandatory)]
    [switch]
    $ShowWindow
  )

  begin
  {
    # determine whether -Online should be made a default
    if ( 
      # user submitted -Name only
      (   $PSBoundParameters.Count -eq 1 -and 
        $PSBoundParameters.ContainsKey('Name')
      ) -or
      # system submitted -Name and -Category (when using -?)
      (
        $PSBoundParameters.Count -eq 2 -and 
        $PSBoundParameters.ContainsKey('Name') -and 
        $PSBoundParameters.ContainsKey('Category')
      )
    )
    {
      # prerequisites are OK, now check whether there IS online help
      # available at all
            
      # retrieve the help URI
      $help = Microsoft.PowerShell.Core\Get-Command -Name $Name 
      # set the -Online parameter only if there is a help URI
      $PSBoundParameters['Online']=
      [string]::IsNullOrWhiteSpace($help.HelpUri) -eq $false
    }
    
    # once the parameter adjustment has been processed, call the original
    # Get-Help cmdlet with the parameters found in $PSBoundParameters
    
    # turn the original Get-Help cmdlet into a proxy command receiving the
    # adjusted parameters
    # with a proxy command, you can invoke its begin, process, and end
    # logic separately. That's required to preserve pipeline functionality
    $cmd = Get-Command -Name 'Get-Help' -CommandType Cmdlet
    $proxy = {& $cmd @PSBoundParameters}.
    GetSteppablePipeline($myInvocation.CommandOrigin)
        
    # now, call its default begin, process, and end blocks in the appropriate 
    # script blocks so it integrates in real-time pipelines
    $proxy.Begin($PSCmdlet)
  }
    
  process { $proxy.Process($_) }
    
  end     { $proxy.End() }
    
  # use the original help taken from Get-Help for this function
  <#
      .ForwardHelpTargetName Microsoft.PowerShell.Core\Get-Help
      .ForwardHelpCategory Cmdlet
  #>
}

When you run this code, the Get-Help function now overwrites the original Get-Help cmdlet. Internally, the function calls the original cmdlet but before this happens, the function checks whether -Online should be made a default parameter.

This now happens only when the user did not submit any conflicting parameters, and only if there is online help available for the requested command in the first place.

Now, whenever you use Get-Help or the common parameter -?, you get rich online help (if available) or else the local default help.

Try this:

 
PS> Get-Service -? 

PS> Connect-IscsiTarget -? 

PS> Get-Help Get-Service

PS> Get-Help Get-Service -ShowWindow  

Since there is online help available for Get-Service, the first call opens a browser window and shows the help. The second call illustrates what happens when there is no online help available: -Online is not made a default parameter here, and instead the default local help shows.

The third and fourth calls illustrate that Get-Help is still fully functional. By default, the command now opens the online help, but if you add other parameters like -ShowWindow, they still work as expected.

If you like this function you should add it to your profile script.

ATTENTION: If you have set any default parameters for Get-Help via $PSDefaultParameter (i.e. when following the instructions in previous tips) then these take effect, and the function above cannot improve anything for you.

Make sure you don’t have any default parameters defined that affect Get-Help:

 
PS> $PSDefaultParameterValues.Keys.ToLower() -like 'get-help*' 

Twitter This Tip! ReTweet this Tip!