Given that Get-ChildItem
has three parameters that begin with the character D
...
❯ (gcm Get-ChildItem).Parameters.Values |? Name -like 'd*' | select name, aliases
Name Aliases
---- -------
Depth {}
Debug {db}
Directory {ad, d}
... and that the Directory
parameter has explicit aliases ad
and d
, why does Get-ChildItem -d
resolve to Get-ChildItem -Depth
?
❯ Get-ChildItem -d
Get-ChildItem: Missing an argument for parameter 'Depth'. Specify a parameter of type 'System.UInt32' and try again.
It's because the other parameters are dynamic parameters added at invocation, based on the path you're asking about.
-Directory
is only valid when the Path
is in the FileSystem provider-DnsName
and -DocumentEncryptionCert
are only valid in the Certificate providerIt's the same with -re
being -Recurse
instead of being ambiguous with -ReadOnly
...
It's also the same with -e
being -Exclude
instead of -Eku
or -ExpiriringInDays
...
And you'll notice that if you run Get-ChildItem -f
and it tells you f
is ambiguous, the only options that it suggests are -Filter
and -Force
, not -File
or -FollowSymlink
which are exclusive to the FileSystem provider...
You can see all of that using Get-Parameter
which you can get from the PowerShell gallery by Install-Script Get-Parameter
I eventually found a way to show by experimentation that the non-dynamic parameters will always get resolved first, and the parameter binder never even looks at the dynamic parameters for anything it can bind without them. Therefore, the parameter selector doesn't even know what the names or aliases of the dynamic parameters are unless it can't find a match on the non-dynamic parameters. So that d
alias is just causing confusion, unless it's being generated in such a way that it also shows up on other commands...
Try this:
using namespace System.Management.Automation
function Test-Alias {
[CmdletBinding()]
param(
[switch]$Awful
)
dynamicparam {
$paramDictionary = [RuntimeDefinedParameterDictionary]::new()
$attribs = [System.Collections.ObjectModel.Collection[System.Attribute]]::new()
$attribs.Add([ParameterAttribute]@{ParameterSetName = "_AllParameterSets" })
$paramdictionary.Add("Automation", [RuntimeDefinedParameter]::new( "Automation", [switch], $attribs))
$attribs += [AliasAttribute]::new("A", "C")
$paramdictionary.Add("Architecture", [RuntimeDefinedParameter]::new( "Architecture", [switch], $attribs))
$paramdictionary
}
end {
$PSBoundParameters
}
}
If you run that in your console, you'll not only be able to see that Test-Alias -A
uses Awful
but also that Test-Alias -C
does work! The A alias never had a chance, but it's not because aliases on dynamic parameters are ignored completely, it's because there was a parameter that started with that letter which was not dynamic.
Now try this:
Trace-Command -Name ParameterBinding { Test-Alias -A } -PSHost
And compare that to the output when you use Test-Alias -C
or Test-Alias -A -C
...
You can see that the Dynamic parameters are only considered after everything non-dynamic that can be bound has been.
Too long for a comment, not a complete answer.
I unfortunately can't find the specific terminology (tab completion, maybe?), but it appears to be part of PowerShell's attempt to be helpful. The behaviour as experienced appears to be "when you use a -d, it attempts to expand the command for you". When running Get-ChildItem -d
and press TAB, it expands to "depth" automatically, even though there's an alias.
This "works" for items without a specific alias as well - the -recurse
toggle (alias 's') works if you use -r
, though it's unspecified.
Expected behaviour, I'd expect the tabbing-out of a work while typing to extend to "depth" as the first preferred word, but simply using the alias should act as "-directory". This would likely require a Microsoft change to correct, if they do, as it may break years of existing scripts.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With