Wednesday, August 21, 2013

Get-Help vs Help - the paging/pager inconsistency

When I'm teaching PowerShell to folks, I often like to use the full name of the cmdlets - get-help instead of 'help', get-childitem instead of 'dir'. You get the idea.

I do this to help drive home the verb-noun syntax and because I can't necessarily expect everyone to know the shortcuts that I tend to use.

Well, the problem that I'm about to describe hasn't come up too much - but its annoying and I finally decided to take a minute and figure out what's going on.

The Problem (Powershell 2.0 or 3.0)

In powershell, if I issue the command:

get-help <cmdlet> -full

then I get a wall of text that is not paged (where paging is that "-- More  --" at the bottom of the screen that allows me to read a page at a time.

To be honest, I'd never really spent more than a second thinking about this annoyance because it seemed so trivial, but since I'm in the midst of putting together a curriculum for a powershell class, it got a little more important.

I had basically made the assumption that help (and man) were simply aliases to get-help. This assumption was wrong.  Have a look:


PS C:\> get-alias -Definition get-help
Get-Alias : This command cannot find a matching alias because alias with definition 'get-help' do n
ot exist.
At line:1 char:10
+ get-alias <<<<  -Definition get-help
    + CategoryInfo          : ObjectNotFound: (get-help:String) [Get-Alias], ItemNotFoundException
    + FullyQualifiedErrorId : ItemNotFoundException,Microsoft.PowerShell.Commands.GetAliasCommand

PS C:\>

That's surprising, no aliases at all for get-help? That makes me wonder what "man" is aliased to. Well as it turns out, "man" is pointing to "help".

PS C:\> get-alias man

CommandType     Name                                      Definition
-----------     ----                                      ----------
Alias           man                                       help

Alright, so what is "help" if it isn't an alias but does the exact same thing as get-help? And what's the deal with "man" aliasing to it?! Let's find out.

PS C:\> Get-Command help

CommandType     Name                                      Definition
-----------     ----                                      ----------
Function        help                                      ...

Ah ha! As we can see (highlighted for dramatic effect) - "help" is not an alias. It is a function! Let's have a look at this function.

PS C:\> cd function:
PS Function:\> dir help

CommandType     Name                                      Definition
-----------     ----                                      ----------
Function        help                                      ...


PS Function:\> Get-Content help

<#
.FORWARDHELPTARGETNAME Get-Help
.FORWARDHELPCATEGORY Cmdlet
#>
[CmdletBinding(DefaultParameterSetName='AllUsersView')]
param(
    [Parameter(Position=0, ValueFromPipelineByPropertyName=$true)]
    [System.String]
    ${Name},

    [System.String]
    ${Path},

    [System.String[]]
    ${Category},

    [System.String[]]
    ${Component},

    [System.String[]]
    ${Functionality},

    [System.String[]]
    ${Role},

    [Parameter(ParameterSetName='DetailedView')]
    [Switch]
    ${Detailed},

    [Parameter(ParameterSetName='AllUsersView')]
    [Switch]
    ${Full},

    [Parameter(ParameterSetName='Examples')]
    [Switch]
    ${Examples},

    [Parameter(ParameterSetName='Parameters')]
    [System.String]
    ${Parameter},

    [Switch]
    ${Online})
$outputEncoding=[System.Console]::OutputEncoding

      Get-Help @PSBoundParameters | more

PS Function:\>

And there we have it! The function (again, highlighted for dramatic effect) simply runs the get-help cmdlet put pipes the output to the "more" command. 

Well - that explains the different behavior, and that's good to know.

Interestingly - the "more" command is also simply a function. Output below for the sake of completeness:

PS Function:\> type more
param([string[]]$paths)
$OutputEncoding = [System.Console]::OutputEncoding

if($paths)
{
    foreach ($file in $paths)
    {
        Get-Content $file | more.com
    }
}
else
{
    $input | more.com
}

Interesting stuff.  I hope if any of you guys wondered about this, you now have the answer and can sleep better at night. :)

No comments:

Post a Comment