如何使 PowerShell 参数完成器仅建议尚未选择的文件?

问题描述 投票:0回答:2

我不确定是否可以这样做,所以首先要解释我想要发生的事情。

我的PowerShell模块函数有这个参数

[ValidateScript({ Test-Path $_ -PathType Leaf })][ValidatePattern("\.xml$")][parameter(Mandatory = $true)][string[]]$PolicyPaths,

它接受多个

.xml
文件。

我一直在使用这个论证完成者:

$ArgumentCompleterPolicyPaths = {
    Get-ChildItem | where-object { $_.extension -like '*.xml' } | foreach-object { return "`"$_`"" }
}
Register-ArgumentCompleter -CommandName "Deploy-SignedWDACConfig" -ParameterName "PolicyPaths" -ScriptBlock $ArgumentCompleterPolicyPaths

一直工作正常。现在我想改进它,以便当我需要从当前工作目录中选择多个

.xml
文件时,并通过按 Tab 开始选择它们(因为我不知道文件名而无需键入任何内容),建议的文件不会'包含我已经选择的那些。我选择它们的方法是先按 Tab 键,然后在每个选定的文件后添加一个逗号
,
,然后再次按 Tab 键从建议中选择另一个。

我尝试了 2 个新的参数完成器,但没有一个按照我想要的方式工作。

这是第一个:

$ArgumentCompleterPolicyPaths = {
  # Get the current command and the already bound parameters
  param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
  # Get the xml files in the current directory
  Get-ChildItem | Where-Object { $_.Extension -like '*.xml' } | ForEach-Object {
    # Check if the file is already selected
    if ($fakeBoundParameters.PolicyPaths -notcontains $_.FullName) {
      # Return the file name with quotes
      return "`"$_`""
    }
  }
}
Register-ArgumentCompleter -CommandName "Deploy-SignedWDACConfig" -ParameterName "PolicyPaths" -ScriptBlock $ArgumentCompleterPolicyPaths

这是第二个:

# Define a class that inherits from ArgumentCompleterAttribute
class XmlFileCompleter : ArgumentCompleterAttribute {
  # Override the GetArgumentCompletionSuggestions method
  [System.Collections.Generic.IEnumerable[System.Management.Automation.CompletionResult]] GetArgumentCompletionSuggestions(
    [System.Management.Automation.CommandAst]$commandAst,
    [System.Management.Automation.CommandParameterAst]$parameterAst,
    [System.Collections.IDictionary]$fakeBoundParameters,
    [System.Management.Automation.Language.IScriptPosition]$cursorPosition
  ) {
    # Get all XML files in the current directory
    $xmlFiles = Get-ChildItem -Path . -Filter *.xml
    # Filter out the files that have already been selected
    $xmlFiles = $xmlFiles | Where-Object { $fakeBoundParameters[$parameterAst.ParameterName] -notcontains $_.Name }
    # Return the file names as completion results
    foreach ($xmlFile in $xmlFiles) {
      [System.Management.Automation.CompletionResult]::new($xmlFile.Name, $xmlFile.Name, 'ParameterValue', $xmlFile.Name)
    }
  }
}

最后在 VS 代码中抛出这个错误:

Unable to find type [ArgumentCompleterAttribute].

P.S 这是我的参数选项卡完成器的当前行为,看看它如何建议我已经选择的相同文件: https://1drv.ms/u/s!AtCaUNAJbbvIhupw8-67jn6ScaBOGw?e=a35FoG

它是 APNG 文件,所以只需将其拖放到浏览器上即可。

powershell powershell-core tab-completion powershell-7.3
2个回答
1
投票
  • 问题是临时绑定参数的字典

    $fakeBoundParameters
    只包含有关other参数的信息如果您还没有键入要完成的非初始数组元素的前缀.

    • 也就是说,使用

      el1
      el2
      表示先前键入/tab 完成的数组元素,在 el1, el2, 之后按
      Tab
      会导致手头的参数 not 作为条目包含在
      $fakeBoundParameters
      中,所以
      el1
      el2
      值不能那样检查。

      • 这是由于传递给参数完成器的
        $fakeBoundParameter
        字典的错误/设计限制,至少达到 PowerShell 7.4.0-preview.3:语法不完整 表达式,例如
        el1, el2,
        似乎阻止包含目前提供的数组元素;请参阅GitHub 问题 #17975
    • 因此,对于在新元素的开头没有键入至少一个字符的情况下用制表符完成的数组参数,先前键入/完成的任何数组元素实际上反映在

      $fakeBoundParameters
      中。

  • workaround是在

    $commandAst
    完成脚本块中搜索传递的命令AST,以查找以
    .xml
    结尾的字符串常量表达式,如下所示:

    • 注意:
      • 至少在假设上,这可能会产生误报,即如果 other 参数也接受以
        .xml
      • 结尾的字符串
      • 在您自己的尝试中,考虑用户在尝试对给定数组元素进行制表符补全之前手动键入的内容;这样做需要更多的工作。
Register-ArgumentCompleter `
  -CommandName Deploy-SignedWDACConfig `
  -ParameterName PolicyPaths `
  -ScriptBlock {
    # Get the current command and the already bound parameters
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)

    # Find all string constants in the AST that end in ".xml"
    $existing = $commandAst.FindAll({ 
        $args[0] -is [System.Management.Automation.Language.StringConstantExpressionAst] -and 
        $args[0].Value -like '*.xml' 
      }, 
      $false
    ).Value  

    # Get the xml files in the current directory
    Get-ChildItem -Filter *.xml | ForEach-Object {
      # Check if the file is already selected
      if ($_.FullName -notin $existing) {
        # Return the file name with quotes
        "`"$_`""
      }
    }
  }

至于你尝试使用

ArgumentCompleterAttribute

  • 你得到一个错误,因为你没有在你的 custom class

     定义中使用 
    full 类型名称作为基类名称,
    System.Management.Automation.ArgumentCompleterAttribute

    • 然而,奇怪的是,省略
      Attribute
      后缀,即
      ArgumentCompleter
      does似乎在没有名称空间限定符的情况下工作。
  • 也就是说,您可以简单地直接使用它,而不是

    子类
    这个ArgumentCompleter,传递与
    Register-ArgumentCompleter
    一起使用的相同脚本块作为属性;也就是说,在您的
    Deploy-SignedWDACConfig
    命令中,您可以使用
    $PolicyPaths
    .
     修饰 
    [ArgumentCompleter({ ... })]

    参数声明

0
投票

当您使用 tab-completion 为具有

ArgumentCompleter
属性的参数选择值时,PowerShell 会将所选值存储在名为
$LASTCOMPLETION
的变量中。
$LASTCOMPLETION
变量是一个自动变量,它包含在 tab-completion 期间选择的最新值。但是
$LASTCOMPLETION
变量可能并不总是可靠的,在某些情况下,它可能包含过时或不正确的值。要解决此问题,您可以使用
$ExecutionContext.SessionState.PSVariable
属性访问
lastCompletionResult
变量,这是另一个自动变量,它还包含在 tab-completion 期间选择的最新值:

[parameter(Mandatory = $true)]
[ArgumentCompleter({
    $exclude = $ExecutionContext.SessionState.PSVariable.GetValue("lastCompletionResult") | Where-Object { $_ -ne $PolicyPaths }
    Get-ChildItem -File -Filter *.xml | Where-Object { $_.Name -notin $exclude } | % Name
})]
[string[]]$PolicyPaths
© www.soinside.com 2019 - 2024. All rights reserved.