【问题标题】:Why does PowerShell's `Get-ChildItem` command resolve the parameter `d` to `Depth`?为什么 PowerShell 的 `Get-ChildItem` 命令将参数 `d` 解析为 `Depth`?
【发布时间】:2021-04-27 11:31:17
【问题描述】:

鉴于Get-ChildItem 具有三个以字符D 开头的参数...

❯ (gcm Get-ChildItem).Parameters.Values |? Name -like 'd*' | select name, aliases

Name      Aliases
----      -------
Depth     {}
Debug     {db}
Directory {ad, d}

...而且Directory 参数有明确的别名add,为什么Get-ChildItem -d 解析为Get-ChildItem -Depth

❯ Get-ChildItem -d
Get-ChildItem: Missing an argument for parameter 'Depth'. Specify a parameter of type 'System.UInt32' and try again.

【问题讨论】:

  • 不直观。 This answer 有一个链接到看似相关的源代码 github.com/PowerShell/PowerShell/blob/…
  • 看起来 "d" 在尝试绑定 i.stack.imgur.com/obwBM.png 时不是可用的别名之一 - 所以它最终以 matchingParameters 集合有 2 项“深度”和“调试”和DeclaredFormalParameters/DynamicParameters 检查仅保留“深度”
  • 如果传递 -ad 别名参数集合有 21 个项目(现在包括 dad)所以看起来使用更多扩展的元数据(作为第一次不匹配任何内容时的第二次传递) - 所以需要研究为什么这 7 个别名不包含在第一遍中
  • @BaconBits 这就是我所期望的,它会由于参数不明确或参数集而引发错误。这正是GetChildItem -f 发生的情况(可以匹配-Filter-File-Force 参数):Parameter cannot be processed because the parameter name 'f' is ambiguous. Possible matches include: -Filter -Force. 请注意,-File 动态参数不是该错误消息的一部分。
  • @BaconBits - 它不是按字母顺序排列的。当该位完成时,matchingParameters github.com/PowerShell/PowerShell/blob/… 中有 2 个值,filteredParameters 中有一个值。但它确实对没有启动的别名有精确匹配的逻辑,因为它在第一次尝试时没有考虑所有别名github.com/PowerShell/PowerShell/blob/…

标签: powershell


【解决方案1】:

评论太长,不是完整的答案。

很遗憾,我找不到具体的术语(可能是制表符补全?),但这似乎是 PowerShell 尝试提供帮助的一部分。所经历的行为似乎是“当您使用 -d 时,它会尝试为您扩展命令”。当运行Get-ChildItem -d 并按 TAB 时,它会自动扩展为“深度”,即使有别名。

这也适用于没有特定别名的项目 - 如果您使用 -r-recurse 切换(别名 's')可以工作,尽管它没有指定。

预期的行为,我希望在键入时将作品的分页扩展到“深度”作为第一个首选单词,但只需使用别名就应该充当“-目录”。如果他们这样做了,这可能需要 Microsoft 进行更改才能更正,因为它可能会破坏多年的现有脚本。

【讨论】:

  • 问题不在于为什么缩写有效,而是为什么缩写优先于别名的完全匹配
  • 我同意,我认为我没有正确表达我的混乱想法。也许这一切都是“按预期”的,但按预期选择的逻辑是应该首先使用完全匹配的别名,而不是部分参数名称。试图在最后一段中提到。如果您觉得(公正的)答案没有任何价值,我可以删除它
  • 我不认为这种行为与 Tab 完成有任何关系,但我相信它会按字母顺序或声明顺序循环;没有“首选”或最佳匹配。还要注意-Directory 不是由选项卡完成建议的,无论如何,可能是因为它是dynamic attribute from the filesystem provider。最后,您说“逻辑......应该首先使用完全匹配的别名”,但观察到的行为恰恰相反。
【解决方案2】:

这是因为其他参数是在调用时添加的动态参数,具体取决于您询问的路径。

  • -Directory 仅在 Path 在文件系统提供程序中时有效
  • -DnsName-DocumentEncryptionCert 仅在证书提供程序中有效

-re-Recurse 相同,而不是与 -ReadOnly 模棱两可...

-e 也是 -Exclude 而不是 -Eku-ExpiriringInDays ...

您会注意到,如果您运行 Get-ChildItem -f 并且它告诉您 f 不明确,那么它建议的唯一选项是 -Filter-Force,而不是 -File-FollowSymlink文件系统提供程序独有...


您可以使用Get-Parameter 查看所有内容,您可以通过Install-Script Get-Parameter 从 PowerShell 库中获取这些信息


我最终找到了一种方法来通过实验证明非动态参数将总是首先得到解决,并且参数绑定器甚至从不查看动态参数以查找没有它们可以绑定的任何东西。因此,参数选择器甚至不知道动态参数的名称或别名是什么除非它无法在非动态参数上找到匹配项。所以 d 别名只会引起混淆,除非它的生成方式也显示在其他命令中......

试试这个:

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

如果您在控制台中运行它,您不仅可以看到Test-Alias -A 使用Awful,而且Test-Alias -C 确实有效! A 别名从来没有机会,但这并不是因为动态参数上的别名被完全忽略,而是因为有一个以那个字母开头的参数不是动态的

现在试试这个:

Trace-Command -Name ParameterBinding { Test-Alias -A } -PSHost

并将其与使用 Test-Alias -CTest-Alias -A -C 时的输出进行比较...

您可以看到,只有在所有非动态可以绑定的东西都已经完成之后,才考虑动态参数。

【讨论】:

  • 那么为目录定义“d”别名有什么价值吗?
  • 没有,但我不能 100% 确定添加它的人是否能说出 ;-)
  • 我添加了一个示例。请记住,在提供程序 cmdlet(如 Get-ChildItem)的情况下,动态参数的定义位置与其他参数(即在提供程序中)完全不同,因此该参数可能在其他命令中使用(我不知道)不这么认为),或者写它的人只是没有注意到。就我个人而言,我认为值得提交一个错误来删除别名(因为结果让 感到困惑,而且我已经使用这种语言很长时间了)...
【解决方案3】:

延伸我的评论。

您看到的-dDepth 的分辨率是 IntelliSense 默认提供的。因此,控制台主机等也会发生同样的事情。

跟踪你的命令,如果你想看看调用堆栈是什么:

Trace-Command -Name metadata,parameterbinding,cmdlet -Expression {Get-ChildItem -d} -PSHost

正如 PowerShell 中存在命令首选项一样,...

about_Command_Precedence

...这同样适用于调用期间的参数、开关。查看 MS PowerShell 源代码,该 cmdlet 也应该为您澄清一些事情:

sn-p

...
dynamicparam
{
    try {
        $targetCmd = $ExecutionContext.InvokeCommand.GetCommand('Microsoft.PowerShell.Management\Get-ChildItem', [System.Management.Automation.CommandTypes]::Cmdlet, $PSBoundParameters)
        $dynamicParams = @($targetCmd.Parameters.GetEnumerator() | Microsoft.PowerShell.Core\Where-Object { $_.Value.IsDynamic })
        if ($dynamicParams.Length -gt 0)
        {
            $paramDictionary = [Management.Automation.RuntimeDefinedParameterDictionary]::new()
            foreach ($param in $dynamicParams)
            {
                $param = $param.Value

                if(-not $MyInvocation.MyCommand.Parameters.ContainsKey($param.Name))
                {
                    $dynParam = [Management.Automation.RuntimeDefinedParameter]::new($param.Name, $param.ParameterType, $param.Attributes)
                    $paramDictionary.Add($param.Name, $dynParam)
                }
            }
            return $paramDictionary
        }
    } catch {
        throw
    }
}
...

【讨论】:

  • 或者,intellisense 是不是很幸运,因为Depth 按字母顺序恰好排在Directory 之前?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-05
  • 2013-03-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多