Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does PowerShell's `Get-ChildItem` command resolve the parameter `d` to `Depth`?

Tags:

powershell

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.
like image 442
Nick Cox Avatar asked Jan 23 '21 00:01

Nick Cox


2 Answers

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 provider

It'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

A screenshot of my output, for the lazy

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.

like image 185
Jaykul Avatar answered Sep 26 '22 00:09

Jaykul


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.

like image 42
Guy S Avatar answered Sep 22 '22 00:09

Guy S