.trim / -trim、.replace / -replace、.split / -split 和其他字符串运算符的获取帮助

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

PowerShell 有一个非常好的内置帮助系统,我经常使用它,我可以使用 Get-Help * 查看所有帮助选项并查询 Cmdlet,或者可以使用 Get-Help about_* 查找主题然后说 Get-Help about_compar* 打开 Comparison Operators 主题,这非常好。

但是,我试图找到如何获得有关各种字符串运算符的帮助,例如 .replace、.compare、.split、.substring。有谁知道如何在 PowerShell 控制台上提取这些主题(它们可能隐藏在一些 about_* 主题中,但我不清楚去哪里查看)?

此外,字符串运算符有 -replace、-compare、-split 变体等,虽然与 .replace 等几乎相同,但一个版本使用正则表达式而另一个不使用。有谁知道是否有帮助主题(同样,可从控制台获得!)阐明所有这些?如果 PowerShell 帮助系统在其内置帮助系统中缺少对所有这些的说明,那么它会感觉非常缺乏,因为这些是该语言中使用非常频繁的部分(所以希望这一切都隐藏在我的一些 about_* 主题中)还没找到)。

  • 如前所述,.NET 类型 的 方法 例如 System.String.Split() 不属于PowerShell,但 PowerShell 所基于的 .NET 框架,因此您必须查阅位于 https://docs.microsoft.com[=122= 的官方文档].

  • 就是说,考虑到 PowerShell 和 .NET 之间的紧密集成,如果能够从 PowerShell 中轻松查阅此文档就好了 - 请参阅 this GitHub suggestion

  • 但是,运算符-splitPowerShell语言特性,并同时发现他们的文档并集中精力,特定于操作员的信息目前很麻烦

    • 运算符等语言特性的文档包含在 about_* 帮助主题中,可以使用 Get-Help -Category HelpFile 作为目标,您可以将其与传递(引用)结合起来用于搜索这些主题的 内容 的操作员名称:

      • 例如,Get-Help -Category HelpFile -Name '-replace'至少缩小了搜索范围,但仍然包括提及的所有帮助主题-replace;结果将包含 about_Operators 帮助主题,它是概述页面,链接到更具体的页面,重点关注运营商的特定 类别 , 比如算术运算符, 比较运算符, ..., 也直接描述了一些通用运算符.
  • 目前没有直接的方法来获取给定运算符的重点信息,但应该有:请参阅 this GitHub suggestion


下面是两个 帮助函数,它们试图克服当前的限制(从 v7.0 开始),Show-OperatorHelpShow-TypeHelp.

注:

  • 这两个函数(必然)不会在控制台(终端)中显示文档,而是在默认[=81]中打开在线文档=]网络浏览器.

    • Show-TypeHelp 中,支持仅限于 .NET 附带的那些 (public) 类型,因此文档页面位于 https://docs.microsoft.com.

    • Show-OperatorHelp 打开特定部分,尽可能只描述感兴趣的运算符;一些运算符,例如算术运算符 +-,没有单独的部分,但在这些情况下,整个主题通常足够短,可以很容易地找到感兴趣的部分。

  • 它们带有基于评论的帮助,因此您可以使用 -?Get-Help 了解更多信息。

  • 例如,您可以将它们放在 $PROFILE 文件中,或者 - 考虑到它们的长度 - 您可能想要创建 外部脚本 ( *.ps1) 基于它们放置在 $env:Path 中列出的目录中。为此,只需删除 function <name> { 行和最后的 } 并将它们保存到 *.ps1 文件中。

示例使用:

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

我建议改为从以下 MIT 许可的 Gists 获取源代码,因为只有它们会得到维护;假设你已经看过代码(我个人可以向你保证它是安全的,但你应该经常检查),你可以 直接安装它们:

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

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

注意:请忽略下面突出显示的损坏语法。

Show-OperatorHelp源代码:

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 + '')
    }
    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源代码:

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
      }
    }
  
  }
}