Get-Help for .trim / -trim, .replace / -replace, .split / -split and other string operators

前端 未结 1 1481
我在风中等你
我在风中等你 2021-01-15 02:46

PowerShell has a pretty good built-in help system that I use a lot and I\'m able to see all help options with Get-Help * and query Cmdlets or can look up Topics

相关标签:
1条回答
  • 2021-01-15 03:14
    • As has been noted, methods of .NET types such as System.String.Split() are not part of PowerShell, but of the .NET framework that PowerShell is built on, so you'll have to consult the official documentation at https://docs.microsoft.com.

    • That said, given the close integration between PowerShell and .NET, it would be nice to be able to easily consult this documentation from within PowerShell - see this GitHub suggestion.

    • However, operators such as -split are PowerShell language features, and both discovering their documentation altogether and getting focused, operator-specific information is currently cumbersome.

      • Documentation for language features such as operators is contained in about_* help topics, which can be targeted with Get-Help -Category HelpFile, and you can combine that with passing the (quoted) name of the operator in order to search the contents of these topics:

        • For example, Get-Help -Category HelpFile -Name '-replace' at least narrows the search down, but still includes all help topics that mention -replace; the result will comprise the about_Operators help topic, which is the overview page that links to more specific pages focused on specific categories of operators, such as arithmetic operators, comparison operators, ..., but also describes some general-purpose operators directly.
    • There is currently no direct way to get focused information for a given operator, but there should be: see this GitHub suggestion.


    Below are two helper functions that try to overcome the current limitations (as of v7.0), Show-OperatorHelp and Show-TypeHelp.

    Note:

    • Both functions (of necessity) don't show the documentation in the console (terminal), but open the online documentation in your default web browser.

      • In Show-TypeHelp, support is limited to those (public) types that ship with .NET and therefore have documentation pages at https://docs.microsoft.com.

      • Show-OperatorHelp opens the specific section describing just the operator of interest whenever possible; some operators, such as the arithmetic operators +, -, don't have individual sections, but the overall topic is usually short enough in these cases to make it easy to find the parts of interest.

    • They come with comment-based help, so you can use -? and Get-Help to learn more about them.

    • You can place them in your $PROFILE file, for instance, or - given their length - you may want to create external scripts (*.ps1) based on them that you place in a directory listed in $env:Path. To that end, simply strip the function <name> { line and the final } and save them to a *.ps1 file.

    Example uses:

    Show-OperatorHelp:

    # Opens the home (overview) page for all PowerShell operators
    Show-OperatorHelp
    
    # Lists all operators in the console, 
    # along with their friendly names and categories.
    Show-OperatorHelp -List
    
    # Opens the help topic (section) for the -replace operator.
    # Equivalent of 
        Show-OperatorHelp -Name '-replace'
    # because you omit the initial '-'; if you do specify it, quote the entire argument.
    Show-OperatorHelp replace
    
    # Opens the help topic (section) for @(...), the array-subexpression operator.
    # Note the need for quoting.
    Show-OperatorHelp '@()'
    

    Show-TypeHelp:

    # Opens the documentation for the System.String.Split() method.
    #  You can specify a type name ('string' in this example) as follows:
    #    * Use a full type name, with the initial 'System.' component optionally omitted (e.g. 'Text.Encoding' for 'System.Text.Encoding'
    #    * Use a type accelerator such as 'xml' for 'System.Xml.XmlDocument'
    # Tab-completion: You can type (part of) a the name of a type
    # (last component of the full name) and cycle through loaded types by that name.
    # E.g., typing `arrayli` tab-completes to 'System.Collections.ArrayList'.
    # Alternatively, *pipe* an instance of a string to the function (see next example).
    Show-TypeHelp string split # Short for: Show-TypeHelp -Type string -Member split
    
    # Opens the documentation for the [Microsoft.PowerShell.Commands.MatchInfo]
    # type, instances of which Select-String outputs.
    'foo' | Select-String o | Show-TypeHelp
    

    I suggest getting the source code from the following MIT-licensed Gists instead, as only they will be maintained; assuming you have looked at the code (which I can personally assure you is safe, but you should always check), you can install them directly:

    irm https://gist.github.com/mklement0/146f3202a810a74cb54a2d353ee4003f/raw/Show-OperatorHelp.ps1 | iex
    
    irm https://gist.github.com/mklement0/50a1b101cd53978cd147b4b138fe6ef4/raw/Show-TypeHelp.ps1 | iex
    

    Note: Ignore the broken syntax highlighting below.

    Show-OperatorHelp source code:

    function Show-OperatorHelp {
      <#
    .SYNOPSIS
    Shows documentation for PowerShell's operators.
    
    .DESCRIPTION
    Navigates to operator-specific or -related online help topics in your default 
    web browser.
    
    Invoke argument-less to see the operators overview help topic.
    -Precedence shows the topic about operator precedence.
    -QuotingRules shows the topic about string literals and quoting.
    -Name <name> targets a specific operator.
    
    .PARAMETER Name
    The name of an operator.
    
    Note that most names must be passed *in quotes* for syntactic reasons;
    e.g., '-match' instead of -match
    However, you may omit the initial '-', in which case you needn't quote.
    
    Use -List to see all names.
    
    .PARAMETER Precedence
    Opens the help topic that describes operator precedence.
    
    .PARAMETER List
    Parameter description
    
    .PARAMETER QuotingRules
    Opens the help topic that describes the quoting rules and syntax for
    string literals.
    
    .PARAMETER CopyUrl
    Instead of opening the topic page in a browser, copies the page's URL to
    the clipboard.
    
    .PARAMETER Version
    Specify a specific PowerShell version number (e.g., 7 or 5.1) for which to
    display the requested help topic.
    By default, the executing engine's version number is used.
    
    .EXAMPLE
    Show-OperatorHelp
    
    Opens the home (overview) page for all PowerShell operators.
    
    .EXAMPLE
    Show-OperatorHelp replace
    
    Opens the help topic (section) for the -replace operator.
    Equivalent of: Show-OperatorHelp -Name '-replace'
    
    .EXAMPLE
    Show-OperatorHelp -List
    
    Lists all operators, along with their friendly names and categories.
    
    .EXAMPLE
    Show-OperatorHelp -Precedence
    
    Shows the help topic about operator precedence.
    #>
    
      [CmdletBinding(DefaultParameterSetName = 'HomePage', SupportsShouldProcess, PositionalBinding = $false)]
      param (
        [Parameter(ParameterSetName = 'Name', Mandatory, Position = 0)]
        [string] $Name
        ,
        [Parameter(ParameterSetName = 'Precedence')]
        [switch] $Precedence
        ,
        [Parameter(ParameterSetName = 'List')]
        [Alias('ListAvailable')]
        [switch] $List
        ,
        [Parameter(ParameterSetName = 'QuotingRules')]
        [Alias('StringLiterals')]
        [switch] $QuotingRules
        ,
        [Parameter(ParameterSetName = 'Name')]
        [Parameter(ParameterSetName = 'Precedence')]
        [Parameter(ParameterSetName = 'QuotingRules')]
        [Parameter(ParameterSetName = 'HomePage')]
        [Alias('cp')]
        [switch] $CopyUrl
        ,
        [Parameter(ParameterSetName = 'Name')]
        [Parameter(ParameterSetName = 'Precedence')]
        [Parameter(ParameterSetName = 'QuotingRules')]
        [Parameter(ParameterSetName = 'HomePage')]
        [string] $Version # PowerShell version
      )
    
      # Default to the executing PowerShell engine's version.
      # Note: If no "?view=powershell-<ver>" query string is present,
      #       the currently highest stable version overall is targeted.
      if ($Version) {
        $verObj = $Version -as [version]
        if (-not $verObj) { $verObj = "$Version.0" -as [version] }
        if (-not $verObj) { Throw "Unrecognized PowerShell version number: $Version" }
      }
      else {
        $verObj = $PSVersionTable.PSVersion
      }
      $Version = ('{0}.{1}' -f $verObj.Major, $verObj.Minor) -replace '\.0$'
    
      $opTable = @{
        # about_Arithmetic_Operators
        '-'            = [pscustomobject] @{ Name = '-'; FriendlyName = 'subtraction / sign inversion'; Topic = 'about_Arithmetic_Operators'; Category = 'Arithmetic' } 
        '*'            = [pscustomobject] @{ Name = '*'; FriendlyName = 'multiplication / string replication'; Topic = 'about_Arithmetic_Operators'; Category = 'Arithmetic' } 
        '/'            = [pscustomobject] @{ Name = '/'; FriendlyName = 'division'; Topic = 'about_Arithmetic_Operators'; Category = 'Arithmetic' } 
        '%'            = [pscustomobject] @{ Name = '%'; FriendlyName = 'modulus'; Topic = 'about_Arithmetic_Operators'; Category = 'Arithmetic' } 
        '+'            = [pscustomobject] @{ Name = '+'; FriendlyName = 'addition / string conatenation'; Topic = 'about_Arithmetic_Operators'; Category = 'Arithmetic' } 
        '-band'        = [pscustomobject] @{ Name = '-band'; FriendlyName = 'bitwise AND'; Topic = 'about_Arithmetic_Operators'; Category = 'Bitwise' } 
        '-bor'         = [pscustomobject] @{ Name = '-bor'; FriendlyName = 'bitwise OR'; Topic = 'about_Arithmetic_Operators'; Category = 'Bitwise' } 
        '-bxor'        = [pscustomobject] @{ Name = '-bxor'; FriendlyName = 'bitwise XOR'; Topic = 'about_Arithmetic_Operators'; Category = 'Bitwise' } 
        '-bNot'        = [pscustomobject] @{ Name = '-bNot'; FriendlyName = 'bitwise complement'; Topic = 'about_Arithmetic_Operators'; Category = 'Bitwise' } 
        # about_Assignment_Operators
        '='            = [pscustomobject] @{ Name = '='; FriendlyName = 'assignment'; Topic = 'about_Assignment_Operators'; Category = 'Assignment' } 
        '+='           = [pscustomobject] @{ Name = '+='; FriendlyName = 'compound assignment'; Topic = 'about_Assignment_Operators'; Category = 'Assignment' } 
        '-='           = [pscustomobject] @{ Name = '-='; FriendlyName = 'compound assignment'; Topic = 'about_Assignment_Operators'; Category = 'Assignment' } 
        '*='           = [pscustomobject] @{ Name = '*='; FriendlyName = 'compound assignment'; Topic = 'about_Assignment_Operators'; Category = 'Assignment' } 
        '/='           = [pscustomobject] @{ Name = '/='; FriendlyName = 'compound assignment'; Topic = 'about_Assignment_Operators'; Category = 'Assignment' } 
        '%='           = [pscustomobject] @{ Name = '%='; FriendlyName = 'compound assignment'; Topic = 'about_Assignment_Operators'; Category = 'Assignment' } 
        '++'           = [pscustomobject] @{ Name = '++'; FriendlyName = 'increment'; Topic = 'about_Assignment_Operators'; Category = 'Assignment' } 
        '--'           = [pscustomobject] @{ Name = '--'; FriendlyName = 'decrement'; Topic = 'about_Assignment_Operators'; Category = 'Assignment' } 
        # about_Comparison_Operators
        '-eq'          = [pscustomobject] @{ Name = '-eq'; FriendlyName = 'equality'; Topic = 'about_Comparison_Operators'; Anchor = '-eq'; Category = 'Equality' } 
        '-ne'          = [pscustomobject] @{ Name = '-ne'; FriendlyName = 'inequality'; Topic = 'about_Comparison_Operators'; Anchor = '-ne'; Category = 'Equality' } 
        '-gt'          = [pscustomobject] @{ Name = '-gt'; FriendlyName = 'greater-than'; Topic = 'about_Comparison_Operators'; Anchor = '-gt'; Category = 'Equality' } 
        '-ge'          = [pscustomobject] @{ Name = '-ge'; FriendlyName = 'greater-than-or-equal'; Topic = 'about_Comparison_Operators'; Anchor = '-gt'; Category = 'Equality' } 
        '-lt'          = [pscustomobject] @{ Name = '-lt'; FriendlyName = 'less-than'; Topic = 'about_Comparison_Operators'; Anchor = '-lt'; Category = 'Equality' } 
        '-le'          = [pscustomobject] @{ Name = '-le'; FriendlyName = 'less-than-or-equal'; Topic = 'about_Comparison_Operators'; Anchor = '-le'; Category = 'Equality' } 
        '-like'        = [pscustomobject] @{ Name = '-like'; FriendlyName = 'wildcard matching'; Topic = 'about_Comparison_Operators'; Anchor = '-like'; Category = 'Matching' } 
        '-notlike'     = [pscustomobject] @{ Name = '-notlike'; FriendlyName = 'negated wildcard matching'; Topic = 'about_Comparison_Operators'; Anchor = '-notlike'; Category = 'Matching' } 
        '-match'       = [pscustomobject] @{ Name = '-match'; FriendlyName = 'regular-expression matching'; Topic = 'about_Comparison_Operators'; Anchor = '-match'; Category = 'Matching' } 
        '-notmatch'    = [pscustomobject] @{ Name = '-notmatch'; FriendlyName = 'negated regular-expression matching'; Topic = 'about_Comparison_Operators'; Anchor = '-notmatch'; Category = 'Matching' } 
        '-replace'     = [pscustomobject] @{ Name = '-replace'; FriendlyName = 'regular-expression-based string replacement'; Topic = 'about_Comparison_Operators'; Anchor = 'replacement-operator'; Category = 'String' } 
        '-in'          = [pscustomobject] @{ Name = '-in'; FriendlyName = 'LHS contained in RHS'; Topic = 'about_Comparison_Operators'; Anchor = '-in'; Category = 'Containment' } 
        '-notIn'       = [pscustomobject] @{ Name = '-notIn'; FriendlyName = 'LHS not contained in collection'; Topic = 'about_Comparison_Operators'; Anchor = '-notin'; Category = 'Containment' } 
        '-contains'    = [pscustomobject] @{ Name = '-contains'; FriendlyName = 'collection contains RHS'; Topic = 'about_Comparison_Operators'; Anchor = '-contains'; Category = 'Containment' } 
        '-notContains' = [pscustomobject] @{ Name = '-notContains'; FriendlyName = 'collection doesn''t contain RHS'; Topic = 'about_Comparison_Operators'; Anchor = '-notcontains'; Category = 'Containment' } 
        # about_Join
        '-join'        = [pscustomobject] @{ Name = '-join'; FriendlyName = 'string joining'; Topic = 'about_Join'; Category = 'String' } 
        # about_Split
        '-split'       = [pscustomobject] @{ Name = '-split'; FriendlyName = 'string splitting'; Topic = 'about_Split'; Category = 'String' } 
        # about_Logical_Operators
        '-not'         = [pscustomobject] @{ Name = '-not'; FriendlyName = 'logical NOT'; Topic = 'about_Logical_Operators'; Category = 'Logical' } 
        '!'            = [pscustomobject] @{ Name = '!'; FriendlyName = 'logical NOT'; Topic = 'about_Logical_Operators'; Category = 'Logical' } 
        '-and'         = [pscustomobject] @{ Name = '-and'; FriendlyName = 'logical AND'; Topic = 'about_Logical_Operators'; Category = 'Logical' } 
        '-or'          = [pscustomobject] @{ Name = '-or'; FriendlyName = 'logical OR'; Topic = 'about_Logical_Operators'; Category = 'Logical' } 
        '-xor'         = [pscustomobject] @{ Name = '-xor'; FriendlyName = 'logical XOR'; Topic = 'about_Logical_Operators'; Category = 'Logical' } 
        # about_Operators
        '$()'          = [pscustomobject] @{ Name = '$()'; FriendlyName = 'subexpression'; Topic = 'about_Operators'; Anchor = 'subexpression-operator--'; Category = 'Evaluation' } 
        '@()'          = [pscustomobject] @{ Name = '@()'; FriendlyName = 'array-subexpression'; Topic = 'about_Operators'; Anchor = 'array-subexpression-operator--'; Category = 'Evaluation' } 
        '()'           = [pscustomobject] @{ Name = '()'; FriendlyName = 'grouping'; Topic = 'about_Operators'; Anchor = 'grouping-operator--'; Category = 'Evaluation' } 
        '. '           = [pscustomobject] @{ Name = '.'; FriendlyName = '(dot-)source'; Topic = 'about_Operators'; Anchor = 'dot-sourcing-operator-'; Category = 'Execution' }   # Sadly, we have to use '. ' to distinguish it from the member-access operator
        '&'            = [pscustomobject] @{ Name = '&'; FriendlyName = 'call (execute)'; Topic = 'about_Operators'; Anchor = 'call-operator-'; Category = 'Execution' }
        ' &'           = [pscustomobject] @{ Name = '&'; FriendlyName = 'background'; Topic = 'about_Operators'; Anchor = 'background-operator-'; Category = 'Execution' } # Sadly, we have to use ' &' to distinguish it from the call operator
        '&&'           = [pscustomobject] @{ Name = '&&'; FriendlyName = 'pipeline-chain AND'; Topic = 'about_Pipeline_Chain_Operators'; Category = 'Pipeline' }
        '||'           = [pscustomobject] @{ Name = '||'; FriendlyName = 'pipeline-chain OR'; Topic = 'about_Pipeline_Chain_Operators'; Category = 'Pipeline' }
        '|'            = [pscustomobject] @{ Name = '|'; FriendlyName = 'pipeline'; Topic = 'about_Operators'; Anchor = 'pipeline-operator-'; Category = 'Pipeline' }
        '.'            = [pscustomobject] @{ Name = '.'; FriendlyName = 'member access'; Topic = 'about_Operators'; Anchor = 'member-access-operator-'; Category = 'Object' } 
        '::'           = [pscustomobject] @{ Name = '::'; FriendlyName = 'static member access'; Topic = 'about_Operators'; Anchor = 'static-member-operator-'; Category = 'Object' } 
        '[0]'          = [pscustomobject] @{ Name = '[0]'; FriendlyName = 'index'; Topic = 'about_Operators'; Anchor = 'index-operator--'; Category = 'Object' } 
        '[int]'        = [pscustomobject] @{ Name = '[int]'; FriendlyName = 'cast / type constraint'; Topic = 'about_Operators'; Anchor = 'cast-operator--'; Category = 'Type' } 
        ','            = [pscustomobject] @{ Name = ','; FriendlyName = 'array constructor'; Topic = 'about_Operators'; Anchor = 'comma-operator-'; Category = 'Array' } 
        '..'           = [pscustomobject] @{ Name = '..'; FriendlyName = 'range (numbers/characters) '; Topic = 'about_Operators'; Anchor = 'range-operator-'; Category = 'Array' } 
        '-f'           = [pscustomobject] @{ Name = '-f'; FriendlyName = 'format (strings)'; Topic = 'about_Operators'; Anchor = 'format-operator--f'; Category = 'String' } 
        '?:'           = [pscustomobject] @{ Name = '?:'; FriendlyName = 'ternary conditional'; Topic = 'about_Operators'; Anchor = 'ternary-operator--if-true--if-false'; Category = 'Conditional' } 
        '??'           = [pscustomobject] @{ Name = '??'; FriendlyName = 'null-coalescing'; Topic = 'about_Operators'; Anchor = ''; Category = 'Conditional' } # ?? Not yet covered in the v7 topic as of 12 Dec 2019
        # about_Redirection
        '>'            = [pscustomobject] @{ Name = '>'; FriendlyName = 'redirection'; Topic = 'about_Redirection'; Category = 'Stream' }
        '>>'           = [pscustomobject] @{ Name = '>>'; FriendlyName = 'appending redirection'; Topic = 'about_Redirection'; Category = 'Stream' }
        # about_Type_Operators
        '-is'          = [pscustomobject] @{ Name = '-is'; FriendlyName = 'type(-inheritance) / interface test'; Topic = 'about_Type_Operators'; Category = 'Type' } 
        '-isnot'       = [pscustomobject] @{ Name = '-isnot'; FriendlyName = 'negated type(-inheritance) / interface test'; Topic = 'about_Type_Operators'; Category = 'Type' } 
        '-as'          = [pscustomobject] @{ Name = '-as'; FriendlyName = 'conditional type conversion'; Topic = 'about_Type_Operators'; Category = 'Type' } 
        # --- Not covered by an operator help topic, but could be considered one.
        # about_Splatting
        '@'            = [pscustomobject] @{ Name = '@'; FriendlyName = 'splatting (arguments)'; Topic = 'about_Splatting'; Category = 'Splatting' } 
      }
    
      # As a courtesy, interpret variations of quotes / quoting styles passed as -Name as if -Quoting had been passed instead.
      $parameterSetNameInEffect = $PSCmdlet.ParameterSetName
      if ($Name -replace '\s' -match '^(?:''''?|""?|@''(''@)?|@"("@)?)$') {
        $parameterSetNameInEffect = 'QuotingRules'  
      }
    
      $url = ''
    
      switch ($parameterSetNameInEffect) {
    
        'Name' {
    
          $warning = ''
    
          # See if the name matches an entry as-is.
          $entry = $opTable[$Name]
    
          # If '.' was passed, warn about member-access / dot-sourcing ambiguity.
          if ($Name -eq '.') {
            $warning = "Defaulting to member-access operator; for the dot-sourcing operator, pass '. '"
          }
          elseif ($Name -eq '&') {
            $warning = "Defaulting to call operator; for the background operator, pass ' &'"
          }
          elseif ($Name.Trim() -eq '@') {
            $warning = "Defaulting to splatting operator; for the array-subexpression operator, pass '@()'; for here-strings, pass '@`"`"@' or -QuotingRules"
          }
          elseif (-not $entry) {
            # Remove any spaces, to support name variations such as '( )', '[ ]'
            $normalizedName = $Name -replace ' '
          }
    
          if (-not $entry) {
            # If not, try prepending "-", to allow users to specify 'replace' instead of '-replace', for instance.
            $entry = $opTable["-$normalizedName"]
          }
          if (-not $entry) {
            # Variations of redirection operators.
            if ($entry -match '^[\d*]?>>?(&\d?)') {
              $entry = $opTable['>']
            }
          }
          if (-not $entry) {
            # Map case variants to their unqualified form; e.g. '-ireplace' -> '-replace'
            $baseName = $normalizedName -replace '^(?=-?)[ci](?=[a-z])'
            if ($baseName -ne $normalizedName) {
              if ($baseName -notlike '-*') { $baseName = '-' + $baseName }
              $entry = $opTable[$baseName]
            }
          }
          if (-not $entry -and $normalizedName -like '`[*`]') {
            # varations of referring to the index / cast / type-constraint operator
            $bracketName = $normalizedName
            if ($bracketName -eq '[]') {
              $bracketName = '[0]' # default to indexer, but warn
              $warning = "Defaulting to index operator; for the cast / type-constraint operators, pass '[int]'"
            }
            elseif ($bracketName -match '^\[(\d+|[''"].*[''"])\]$') {
              $bracketName = '[0]' # indexer - numeric or string
            }
            else {
              $bracketName = '[int]' # cast
            }
            $entry = $opTable[$bracketName]
          }
    
          if (-not $entry) {
            Throw "Not a recognized operator: $Name"
          }
          elseif ($warning) {
            Write-Warning $warning
          }
    
          $url = "https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/" + $entry.Topic
          if ($entry.Anchor) {
            $url += '#' + $entry.Anchor
          }
          break
        }
    
        'Precedence' {
          $url = 'https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_Operator_Precedence'
          break
        }
    
        'QuotingRules' {
          $url = 'https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_Quoting_Rules'
          break
        }
    
        'HomePage' {
          $url = 'https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_Operators'
          break
        }
    
        'List' {
          # List the operators table below.
        }
    
        Default { Throw "What are you doing here?" }
      }
    
      if ($url -and $Version) {
        $versionQueryString = '?view=powershell-' + $Version
        if ($url.Contains('#')) {
          $url = $url -replace '(#.+)$', ($versionQueryString + '$1')
        }
        else {
          $url += $versionQueryString
        }
      }
    
      # -WhatIf support.
      if (-not $PSCmdlet.ShouldProcess((("`n" + $url + "`n"), "List all operators")[$parameterSetNameInEffect -eq 'List'])) { return }
    
      if ($parameterSetNameInEffect -eq 'List') {
    
        $opTable.Values | Select-Object Name, FriendlyName, Category | Sort-Object Category, Name
    
      }
      else {
    
        if ($CopyUrl) {
          Write-Verbose "Copying URL to clipboard: $url"
          Set-Clipboard $url
        }
        else {
          Write-Verbose "Navigating to: $url"
          Start-Process $url
        }
    
      }
    }
    

    Show-TypeHelp source code:

    function Show-TypeHelp {
      <#
      .SYNOPSIS
      Shows documentation for built-in .NET types.
    
      .DESCRIPTION
      Navigates to the specified .NET type's docs.microsoft.com documentation page
      in your default web browser, assuming the type comes with .NET.
    
      Use -WhatIf to preview the URL that would be opened.
    
      There are two basic invocation patterns:
    
      * Provide the full name of a type a type accelerator or a [type] instance
        via the -Type parameter.
    
         * Tab-completion works with the (prefixes of) a type's simple name (without
          namespace component); e.g., Get-TypeHelp List<tab> cycles through all
          loaded types whose name is or starts with 'List'.
    
      * Pipe instance(s) of the type of interest.
    
      .PARAMETER Type
      Can be the name of a type (e.g. "string"), or a type literal (e.g. [string]).
      
      If given a name, the name must be one of the following:
       * The type's full name; e.g., 'System.Xml.XmlDocument'.
       * The full name with the 'System.' prefix omitted; e.g., 'Xml.XmlDocument'
       * The name of a PowerShell type accelerator; e.g., 'xml'
    
      .PARAMETER Member
      The optional name of a property or method to get specific information on.
      If the target type has no such member, a warning is issued, and you're taken
      to the type's home page.
    
      .PARAMETER InputObject
      Object(s), typically provided via the pipeline, whose type's documentation
      page should be opened.
    
      .PARAMETER Platform
      The target .NET platform / standard, which must include a specific major.minor
      version number; e.g., 'dotnetcore-3.1'.
      Currently (v7.0), the latest 'netframework-*' version is targeted by default, i.e.,
      the Windows-only .NET Framework (FullCLR).
    
      Use tab completion to cycle through the available platforms, but note that
      you must complete the specific version number yourself.
    
      .EXAMPLE
      Get-TypeHelp xml
    
      Opens the documentation page for type [xml], i.e., for System.Xml.XmlDocument
    
      .EXAMPLE
      Get-TypeHelp string split
    
      Opens the documentation page for the System.String type's Split() method.
    
      .EXAMPLE
      Get-Item / | Get-TypeHelp
    
      Opens the documentation page for type System.IO.DirectoryInfo, an instance
      of which is output by the Get-Item command.
    
      .EXAMPLE
      Get-TypeHelp regex -Platform netcore-3.1
    
      Opens the documenation page for type System.Text.RegularExpressions.Regex
      for the .NET Core 3.1 platform.
      #>
    
      [CmdletBinding(DefaultParameterSetName = 'ByType', SupportsShouldProcess = $true)]
      [OutputType()] # No output.
      param(
        [Parameter(ParameterSetName = 'ByType', Mandatory, Position = 0)]
        [ArgumentCompleter( {
            param($cmd, $param, $wordToComplete)
            # Remove enclosing / opening quote(s), if present.
            $wordToComplete = $wordToComplete -replace '^[''"]|[''"]$'
            if ($tp = $wordToComplete -as [Type]) {
              # Already a full type name or the name of a type accelerator such as [xml]
              $tp.FullName
            }
            else {
              # Get the full names of all public types (including nested ones), but exclude dynamic assemblies.
              # (Dynamic assemblies can't be expected to have documentation pages anyway; also, not excluding them would break the .GetExportedTypes() call.)
              $allLoadedTypes = [System.AppDomain]::CurrentDomain.GetAssemblies().Where( { -not $_.IsDynamic }).GetExportedTypes().FullName
              # Prefix-name-only-match against all loaded full type names from non-dynamic assemblies at
              # and enclose in embedded '...' if the type name contains a ` char. (generics), then sort.
              $(foreach ($match in $allLoadedTypes -match "[+.]$wordToComplete[^.]*$") {
                  ($match, "'$match'")[$match -match '`']
                }) | Sort-Object
            }
          })]
        [Type] $Type
        ,
        [Parameter(ParameterSetName = 'ByType', Position = 1)]
        [string] $Member
        ,
        [Parameter(ParameterSetName = 'ByInstance', ValueFromPipeline, Mandatory)]
        [ValidateNotNullOrEmpty()]
        $InputObject
        ,
        [ArgumentCompleter( {
            'netcore-', 'netframework-', 'xamarinmac-', 'dotnet-plat-ext-', 'netstandard-', 'dotnet-uwp-', 'xamarinandroid-', 'xamarinios-10.8', 'xamarinmac-' -like "$wordToComplete*"
          })]
        [string] $Platform
        ,
        [Alias('cp')]
        [switch] $CopyUrl
      )
      
      begin {
        $types = [System.Collections.Generic.List[Type]]::new()
        $instances = [System.Collections.Generic.List[object]]::new()
      
        if ($Platform -and $Platform -notmatch '^[a-z][a-z-]+-\d+\.\d+$') {
          Throw "The -Platform value must be in the form '<platform-id>-<major>.<minor>'; e.g., 'netcore-3.1'; use tab completion to cycle through the supported platforms and add a version number."
        }
      }
      process {
        switch ($PSCmdlet.ParameterSetName) {
          'ByType' { $types.Add($Type) }
          'ByInstance' { $instances.Add($InputObject) }
          Default { Throw 'What are you doing here?' }
        }
      }
      end {
      
        # If instances were given, determine their types now.
        if ($PSCmdlet.ParameterSetName -eq 'ByInstance') {
          $types = $instances.ToArray().ForEach('GetType') | Select-Object -Unique
        }
    
        $urls = foreach ($tp in $types) {
          # Make sure that the member exists, otherwise a 404 happens.
          if ($Member -and $tp.GetMembers().Name -notcontains $Member) {
            Write-Warning "Ignoring member name '$Member', because type '$tp' has no such member."
            $Member = ''
          }
          # Transform the full type name to the format used in the URLs.
          # '`1' -> '-1'
          # System.Environment+SpecialFolder -> 'System.Environment.SpecialFolder'
          $typeNameForUrl = $tp.FullName -replace '`', '-' -replace '\+', '.'
          "https://docs.microsoft.com/$PSCulture/dotnet/api/$typeNameForUrl" + ('', ".$Member")[$Member -ne ''] + ('', "?view=$Platform")[[bool] $Platform]
        }
      
        if ($PSCmdlet.ShouldProcess("`n" + ($urls -join "`n") + "`n")) { 
          if ($CopyUrl) {
            Write-Verbose "Copying URL(s) to clipboard: $urls"
            Set-Clipboard $urls
          }
          else {
            Write-Verbose "Navigating to: $urls"
            Start-Process $urls
          }
        }
      
      }
    }
    
    0 讨论(0)
提交回复
热议问题