Skip to content

Instantly share code, notes, and snippets.

@Trucido
Created November 23, 2018 09:17
Show Gist options
  • Save Trucido/3691286368432a260aa77cba0c638436 to your computer and use it in GitHub Desktop.
Save Trucido/3691286368432a260aa77cba0c638436 to your computer and use it in GitHub Desktop.
<#
.Synopsis
Enumerates the parameters of one or more commands.
.Description
Lists all the parameters of a command, by ParameterSet, including their aliases, type, etc.
By default, formats the output to tables grouped by command and parameter set.
.Parameter CommandName
The name of the command to get parameters for.
.Parameter ParameterName
Wilcard-enabled filter for parameter names.
.Parameter ModuleName
The name of the module which contains the command (this is for scoping)
.Parameter SkipProviderParameters
Skip testing for Provider parameters (will be much faster)
.Parameter SetName
The ParameterSet name to filter by (allows wildcards)
.Parameter Force
Forces including the CommonParameters in the output.
.Example
Get-Command Select-Xml | Get-Parameter
.Example
Get-Parameter Select-Xml
#>
function Get-Parameter {
[CmdletBinding(DefaultParameterSetName="ParameterName")]
param(
[Parameter(Position = 1, Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
[Alias("Name")]
[string[]]$CommandName,
[Parameter(Position = 2, ValueFromPipelineByPropertyName=$true, ParameterSetName="FilterNames")]
[string[]]$ParameterName = "*",
[Parameter(ValueFromPipelineByPropertyName=$true, ParameterSetName="FilterSets")]
[string[]]$SetName = "*",
[Parameter(ValueFromPipelineByPropertyName = $true)]
$ModuleName,
[Switch]$SkipProviderParameters,
[switch]$Force
)
begin {
$PropertySet = @( "Name",
@{n="Position";e={if($_.Position -lt 0){"Named"}else{$_.Position}}},
"Aliases",
@{n="Short";e={$_.Name}},
@{n="Type";e={$_.ParameterType.Name}},
@{n="ParameterSet";e={$paramset}},
@{n="Command";e={$command}},
@{n="Mandatory";e={$_.IsMandatory}},
@{n="Provider";e={$_.DynamicProvider}},
@{n="ValueFromPipeline";e={$_.ValueFromPipeline}},
@{n="ValueFromPipelineByPropertyName";e={$_.ValueFromPipelineByPropertyName}}
)
function Join-Object {
Param(
[Parameter(Position=0)]
$First,
[Parameter(ValueFromPipeline=$true,Position=1)]
$Second
)
begin {
[string[]] $p1 = $First | Get-Member -MemberType Properties | Select-Object -ExpandProperty Name
}
process {
$Output = $First | Select-Object $p1
foreach ($p in $Second | Get-Member -MemberType Properties | Where-Object {$p1 -notcontains $_.Name} | Select-Object -ExpandProperty Name) {
Add-Member -InputObject $Output -MemberType NoteProperty -Name $p -Value $Second."$p"
}
$Output
}
}
function Add-Parameters {
[CmdletBinding()]
param(
[Parameter(Position=0)]
[Hashtable]$Parameters,
[Parameter(Position=1)]
[System.Management.Automation.ParameterMetadata[]]$MoreParameters,
[Parameter(Position=2)]
[System.Management.Automation.ProviderInfo]$Provider
)
foreach ($p in $MoreParameters | Where-Object { !$Parameters.ContainsKey($_.Name) } ) {
Write-Debug ("INITIALLY: " + $p.Name)
$Parameters.($p.Name) = $p | Select *
}
if ($Provider) {
[Array]$Dynamic = $MoreParameters | Where-Object { $_.IsDynamic }
if ($dynamic) {
foreach ($d in $dynamic) {
if (Get-Member -InputObject $Parameters.($d.Name) -Name DynamicProvider) {
Write-Debug ("ADD:" + $d.Name + " " + $Provider.Name)
$Parameters.($d.Name).DynamicProvider += $Provider.Name
} else {
Write-Debug ("CREATE:" + $d.Name + " " + $Provider.Name)
$Parameters.($d.Name) = $Parameters.($d.Name) | Select *, @{ n="DynamicProvider";e={ @($Provider.Name) } }
}
}
}
}
}
}
process {
foreach ($cmd in $CommandName) {
if ($ModuleName) {$cmd = "$ModuleName\$cmd"}
Write-Verbose "Searching for $cmd"
$commands = @(Get-Command $cmd)
foreach ($command in $commands) {
Write-Verbose "Searching for $command"
# resolve aliases (an alias can point to another alias)
while ($command.CommandType -eq "Alias") {
$command = @(Get-Command ($command.definition))[0]
}
if (-not $command) {continue}
if ($PSVersionTable.PSVersion.Major -ge 5) {
Write-Verbose "Get-Parameters for $($Command.Source)\$($Command.Name)"
$isCoreCommand = $Command.Source -eq "Microsoft.PowerShell.Management"
} else {
Write-Verbose "Get-Parameters for $($Command.ModuleName)\$($Command.Name)"
$isCoreCommand = $Command.ModuleName -eq "Microsoft.PowerShell.Management"
}
$Parameters = @{}
## We need to detect provider parameters ...
$NoProviderParameters = !$SkipProviderParameters
## Shortcut: assume only the core commands get Provider dynamic parameters
if(!$SkipProviderParameters -and $isCoreCommand) {
## The best I can do is to validate that the command has a parameter which could accept a string path
foreach($param in $Command.Parameters.Values) {
if(([String[]],[String] -contains $param.ParameterType) -and ($param.ParameterSets.Values | Where { $_.Position -ge 0 })) {
$NoProviderParameters = $false
break
}
}
}
if($NoProviderParameters) {
if($Command.Parameters) {
Add-Parameters $Parameters $Command.Parameters.Values
}
} else {
foreach ($provider in Get-PSProvider) {
if($provider.Drives.Count -gt 0) {
$drive = Get-Location -PSProvider $Provider.Name
} else {
$drive = "{0}\{1}::\" -f $provider.ModuleName, $provider.Name
}
Write-Verbose ("Get-Command $command -Args $drive | Select -Expand Parameters")
$MoreParameters = @()
try {
$MoreParameters = (Get-Command $command -Args $drive).Parameters.Values
} catch {}
if($MoreParameters.Count -gt 0) {
Add-Parameters $Parameters $MoreParameters $provider
}
}
# If for some reason none of the drive paths worked, just use the default parameters
if($Parameters.Count -eq 0) {
if($Command.Parameters) {
Add-Parameters $Parameters $Command.Parameters.Values $provider
}
}
}
## Calculate the shortest distinct parameter name -- do this BEFORE removing the common parameters or else.
$Aliases = $Parameters.Values | Select-Object -ExpandProperty Aliases ## Get defined aliases
$ParameterNames = $Parameters.Keys + $Aliases
foreach ($p in $($Parameters.Keys)) {
$short = "^"
$aliases = @($p) + @($Parameters.$p.Aliases) | sort { $_.Length }
$shortest = "^" + @($aliases)[0]
foreach($name in $aliases) {
$short = "^"
foreach ($char in [char[]]$name) {
$short += $char
$mCount = ($ParameterNames -match $short).Count
if ($mCount -eq 1 ) {
if($short.Length -lt $shortest.Length) {
$shortest = $short
}
break
}
}
}
if($shortest.Length -lt @($aliases)[0].Length +1){
# Overwrite the Aliases with this new value
$Parameters.$p = $Parameters.$p | Add-Member NoteProperty Aliases ($Parameters.$p.Aliases + @("$($shortest.SubString(1))*")) -Force -Passthru
}
}
# Write-Verbose "Parameters: $($Parameters.Count)`n $($Parameters | ft | out-string)"
$CommonParameters = [string[]][System.Management.Automation.Cmdlet]::CommonParameters
foreach ($paramset in @($command.ParameterSets | Select-Object -ExpandProperty "Name")) {
$paramset = $paramset | Add-Member -Name IsDefault -MemberType NoteProperty -Value ($paramset -eq $command.DefaultParameterSet) -PassThru
foreach ($parameter in $Parameters.Keys | Sort-Object) {
# Write-Verbose "Parameter: $Parameter"
if (!$Force -and ($CommonParameters -contains $Parameter)) {continue}
if ($Parameters.$Parameter.ParameterSets.ContainsKey($paramset) -or $Parameters.$Parameter.ParameterSets.ContainsKey("__AllParameterSets")) {
if ($Parameters.$Parameter.ParameterSets.ContainsKey($paramset)) {
$output = Join-Object $Parameters.$Parameter $Parameters.$Parameter.ParameterSets.$paramSet
} else {
$output = Join-Object $Parameters.$Parameter $Parameters.$Parameter.ParameterSets.__AllParameterSets
}
Write-Output $Output | Select-Object $PropertySet | ForEach-Object {
$null = $_.PSTypeNames.Insert(0,"System.Management.Automation.ParameterMetadata")
$null = $_.PSTypeNames.Insert(0,"System.Management.Automation.ParameterMetadataEx")
# Write-Verbose "$(($_.PSTypeNames.GetEnumerator()) -join ", ")"
$_
} |
Add-Member ScriptMethod ToString { $this.Name } -Force -Passthru |
Where-Object {$(foreach($pn in $ParameterName) {$_ -like $Pn}) -contains $true} |
Where-Object {$(foreach($sn in $SetName) {$_.ParameterSet -like $sn}) -contains $true}
}
}
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment