Last active
September 24, 2021 20:52
-
-
Save mklement0/146f3202a810a74cb54a2d353ee4003f to your computer and use it in GitHub Desktop.
PowerShell function for performing online documentation lookups for PowerShell's operators and operator-like symbols - see https://stackoverflow.com/a/59324892/45375
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<# | |
Prerequisites: PowerShell v3+ | |
License: MIT | |
Author: Michael Klement <[email protected]> | |
DOWNLOAD and DEFINITION OF THE FUNCTION: | |
irm https://gist.github.com/mklement0/146f3202a810a74cb54a2d353ee4003f/raw/Show-OperatorHelp.ps1 | iex | |
The above directly defines the function below in your session and offers guidance for making it available in future | |
sessions too. | |
DOWNLOAD ONLY: | |
irm https://gist.github.com/mklement0/146f3202a810a74cb54a2d353ee4003f/raw > Show-OperatorHelp.ps1 | |
Thea above downloads to the specified file, which you then need to dot-source to make the function available | |
in the current session: | |
. ./Show-OperatorHelp.ps1 | |
To learn what the function does: | |
* see the next comment block | |
* or, once downloaded and defined, invoke the function with -? or pass its name to Get-Help. | |
To define an ALIAS for the function, (also) add something like the following to your $PROFILE: | |
Set-Alias shoh 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. | |
Disambiguation: | |
* Use '[0]' for the indexing operator, and '[int]' for the cast / type-constraint | |
operator. | |
* Use '.' for the member-access operator, and '. ' for the dot-sourcing operator. | |
* Use '&' for the call operator, and ' &' for the background operator. | |
* Use '@' for the splatting operator, and '@()' for the array-subexpression | |
operator. | |
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 CopyLink | |
Instead of opening the topic page in the default browser, copies a Markdown | |
link to it to the clipboard; e.g.: | |
`.`, the [member access operator](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_Operators#member-access-operator-) | |
.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. | |
.EXAMPLE | |
Show-OperatorHelp like -CopyLink | |
Copies a Markdown link to the online help topic for the -like operator to | |
the clipboard. | |
.NOTES | |
License: MIT | |
Author: Michael Klement <[email protected]> | |
Showing local, in-console information is currently not possible, because | |
the text-only help topics do not allow for targeted navigation the way that | |
the online topics do. | |
#> | |
[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] $CopyLink | |
, | |
[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. | |
$explicitVersionRequested = $PSBoundParameters.ContainsKey('Version') | |
if ($explicitVersionRequested) { | |
$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$' | |
# Table gleaned from https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_Operator_Precedence, with subsequent cleanup and massaging: | |
# * In-page anchors (HTML) determined manually. | |
# * .Category value is a custom categorization loosely based on the wording in the help topics. | |
$opTable = [ordered] @{ | |
# 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 concatenation'; Topic = 'about_Arithmetic_Operators'; Category = 'Arithmetic' } | |
'-shl' = [pscustomobject] @{ Name = '-shl'; FriendlyName = 'bitwise left-shift'; Topic = 'about_Arithmetic_Operators'; Anchor = 'bitwise-operators'; Category = 'Bitwise' } | |
'-shr' = [pscustomobject] @{ Name = '-shr'; FriendlyName = 'bitwise right-shift'; Topic = 'about_Arithmetic_Operators'; Anchor = 'bitwise-operators'; Category = 'Bitwise' } | |
'-band' = [pscustomobject] @{ Name = '-band'; FriendlyName = 'bitwise AND'; Topic = 'about_Arithmetic_Operators'; Anchor = 'bitwise-operators'; Category = 'Bitwise' } | |
'-bor' = [pscustomobject] @{ Name = '-bor'; FriendlyName = 'bitwise OR'; Topic = 'about_Arithmetic_Operators'; Anchor = 'bitwise-operators'; Category = 'Bitwise' } | |
'-bxor' = [pscustomobject] @{ Name = '-bxor'; FriendlyName = 'bitwise XOR'; Topic = 'about_Arithmetic_Operators'; Anchor = 'bitwise-operators'; Category = 'Bitwise' } | |
'-bNot' = [pscustomobject] @{ Name = '-bNot'; FriendlyName = 'bitwise complement'; Topic = 'about_Arithmetic_Operators'; Anchor = 'bitwise-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-and--notmatch'; Category = 'Matching' } | |
'-notmatch' = [pscustomobject] @{ Name = '-notmatch'; FriendlyName = 'negated regular-expression matching'; Topic = 'about_Comparison_Operators'; Anchor = '-match-and--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-sourcing'; Topic = 'about_Operators'; Anchor = 'dot-sourcing-operator-'; Category = 'Execution'; LookupName = "'. '" } # Sadly, we have to use '. ' to distinguish it from the member-access operator | |
'&' = [pscustomobject] @{ Name = '&'; FriendlyName = 'call'; Topic = 'about_Operators'; Anchor = 'call-operator-'; Category = 'Execution' } | |
' &' = [pscustomobject] @{ Name = '&'; FriendlyName = 'background'; Topic = 'about_Operators'; Anchor = 'background-operator-'; Category = 'Execution'; LookupName = "' &'" } # 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'; Topic = 'about_Operators'; Anchor = 'range-operator-'; Category = 'Array' } | |
'-f' = [pscustomobject] @{ Name = '-f'; FriendlyName = 'format'; 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' } # v7+ | |
'??' = [pscustomobject] @{ Name = '??'; FriendlyName = 'null-coalescing'; Topic = 'about_Operators'; Anchor = 'null-coalescing-operator-'; Category = 'Conditional' } # v7+ | |
'??=' = [pscustomobject] @{ Name = '??='; FriendlyName = 'null-coalescing assignment'; Topic = 'about_Operators'; Anchor = 'null-coalescing-assignment-operator-'; Category = 'Conditional' } # v7+ | |
'?.' = [pscustomobject] @{ Name = '?.'; FriendlyName = 'null-conditional'; Topic = 'about_Operators'; Anchor = 'null-conditional-operators--and-'; Category = 'Conditional' } # v7.1+ | |
'?[]' = [pscustomobject] @{ Name = '?[]'; FriendlyName = 'null-conditional'; Topic = 'about_Operators'; Anchor = 'null-conditional-operators--and-'; Category = 'Conditional' } # v7.1+ | |
# 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'; Topic = 'about_Splatting'; Category = 'ArgumentPassing' } | |
# | |
'--%' = [pscustomobject] @{ Name = '--%'; FriendlyName = 'stop-parsing symbol'; Topic = 'about_Parsing'; Category = 'ArgumentPassing' } | |
} | |
# 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. | |
# Note: We don't trim the name yet, because we need to suport '. ' to | |
# disambiguate the dot-sourcing from the member-access operaror. | |
$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?" } | |
} | |
# Insert a query-string component fo the PS version whose version-specific topic to show. | |
# When copying a Markdown link, we only include a version if one was explicitly passed. | |
if ($url -and ($explicitVersionRequested -or -not $CopyLink)) { | |
$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, LookupName | Sort-Object Category, Name | |
} | |
else { | |
if ($CopyLink) { | |
# Construct a Markdown link. | |
$mdLink = '`{0}`, the [{1} operator]({2})' -f $entry.Name, $entry.FriendlyName, $url | |
# Copy to the clipboard. | |
Write-Verbose "Copying URL as Markdown links to clipboard: $mdLink" | |
Set-Clipboard $mdLink | |
} | |
else { | |
Write-Verbose "Navigating to: $url" | |
Start-Process $url | |
} | |
} | |
} | |
# -------------------------------- | |
# GENERIC INSTALLATION HELPER CODE | |
# -------------------------------- | |
# Provides guidance for making the function persistently available when | |
# this script is either directly invoked from the originating Gist or | |
# dot-sourced after download. | |
# IMPORTANT: | |
# * DO NOT USE `exit` in the code below, because it would exit | |
# the calling shell when Invoke-Expression is used to directly | |
# execute this script's content from GitHub. | |
# * Because the typical invocation is DOT-SOURCED (via Invoke-Expression), | |
# do not define variables or alter the session state via Set-StrictMode, ... | |
# *except in child scopes*, via & { ... } | |
if ($MyInvocation.Line -eq '') { | |
# Most likely, this code is being executed via Invoke-Expression directly | |
# from gist.github.com | |
# To simulate for testing with a local script, use the following: | |
# Note: Be sure to use a path and to use "/" as the separator. | |
# iex (Get-Content -Raw ./script.ps1) | |
# Derive the function name from the invocation command, via the enclosing | |
# script name presumed to be contained in the URL. | |
# NOTE: Unfortunately, when invoked via Invoke-Expression, $MyInvocation.MyCommand.ScriptBlock | |
# with the actual script content is NOT available, so we cannot extract | |
# the function name this way. | |
& { | |
param($invocationCmdLine) | |
# Try to extract the function name from the URL. | |
$funcName = $invocationCmdLine -replace '^.+/(.+?)(?:\.ps1).*$', '$1' | |
if ($funcName -eq $invocationCmdLine) { | |
# Function name could not be extracted, just provide a generic message. | |
# Note: Hypothetically, we could try to extract the Gist ID from the URL | |
# and use the REST API to determine the first filename. | |
Write-Verbose -Verbose "Function is now defined in this session." | |
} | |
else { | |
# Indicate that the function is now defined and also show how to | |
# add it to the $PROFILE or convert it to a script file. | |
Write-Verbose -Verbose @" | |
Function `"$funcName`" is now defined in this session. | |
* If you want to add this function to your `$PROFILE, run the following: | |
"``nfunction $funcName {``n`${function:$funcName}``n}" | Add-Content `$PROFILE | |
* If you want to convert this function into a script file that you can invoke | |
directly, run: | |
"`${function:$funcName}" | Set-Content $funcName.ps1 -Encoding $('utf8' + ('', 'bom')[[bool] (Get-Variable -ErrorAction Ignore IsCoreCLR -ValueOnly)]) | |
"@ | |
} | |
} $MyInvocation.MyCommand.Definition # Pass the original invocation command line to the script block. | |
} | |
else { | |
# Invocation presumably as a local file after manual download, | |
# either dot-sourced (as it should be) or mistakenly directly. | |
& { | |
param($originalInvocation) | |
# Parse this file to reliably extract the name of the embedded function, | |
# irrespective of the name of the script file. | |
$ast = $originalInvocation.MyCommand.ScriptBlock.Ast | |
$funcName = $ast.Find( { $args[0] -is [System.Management.Automation.Language.FunctionDefinitionAst] }, $false).Name | |
if ($originalInvocation.InvocationName -eq '.') { | |
# Being dot-sourced as a file. | |
# Provide a hint that the function is now loaded and provide | |
# guidance for how to add it to the $PROFILE. | |
Write-Verbose -Verbose @" | |
Function `"$funcName`" is now defined in this session. | |
If you want to add this function to your `$PROFILE, run the following: | |
"``nfunction $funcName {``n`${function:$funcName}``n}" | Add-Content `$PROFILE | |
"@ | |
} | |
else { | |
# Mistakenly directly invoked. | |
# Issue a warning that the function definition didn't effect and | |
# provide guidance for reinvocation and adding to the $PROFILE. | |
Write-Warning @" | |
This script contains a definition for function "$funcName", but this definition | |
only takes effect if you dot-source this script. | |
To define this function for the current session, run: | |
. "$($originalInvocation.MyCommand.Path)" | |
"@ | |
} | |
} $MyInvocation # Pass the original invocation info to the helper script block. | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment