PowerShell 二进制模块:为 Cmdlet 参数值提供动态选项卡完成。

3
我正在使用C#编写二进制Powershell模块,并希望拥有一个Cmdlet参数,提供动态的、运行时的选项卡自动完成。然而,我很难弄清楚如何在二进制模块中实现这一点。以下是我尝试使其工作的方法:
using System;
using System.Collections.ObjectModel;
using System.Management.Automation;

namespace DynamicParameterCmdlet
{

    [Cmdlet("Say", "Hello")]
    public class MyCmdlet : PSCmdlet
    {

        [Parameter, PSTypeName("string")]
        public RuntimeDefinedParameter Name { get; set; }

        public MyCmdlet() : base() {
            Collection<Attribute> attributes = new Collection<Attribute>() {
                new ParameterAttribute()
            };

            string[] allowedNames = NameProvider.GetAllowedNames();
            attributes.Add(new ValidateSetAttribute(allowedNames));
            Name = new RuntimeDefinedParameter("Name", typeof(string), attributes);
        }

        protected override void ProcessRecord()
        {
            string name = (string)Name.Value;
            WriteObject($"Hello, {Name}");
        }
    }

    public static class NameProvider
    {
        public static string[] GetAllowedNames()
        {
            // Hard-coded array here for simplicity but imagine in reality this
            // would vary at run-time
            return new string[] { "Alice", "Bob", "Charlie" };
        }
    }
}

这不起作用。我没有任何选项完成功能。我还收到一个错误:
PS > Say-Hello -Name Alice
Say-Hello : Cannot bind parameter 'Name'. Cannot convert the "Alice" value of type "System.String" to type "System.Management.Automation.RuntimeDefinedParameter".
At line:1 char:17
+ Say-Hello -Name Alice
+                 ~~~~~
    + CategoryInfo          : InvalidArgument: (:) [Say-Hello], ParameterBindingException
    + FullyQualifiedErrorId : CannotConvertArgumentNoMessage,DynamicParameterCmdlet.MyCmdlet

我找到了一个文章,其中有一个非二进制Powershell模块的示例。在非二进制模块中,您需要包括DynamicParam,然后是构建并返回RuntimeParameterDictionary对象的语句。基于这个示例,我期望在PSCmdlet类中有相应的方法,可能是可重写的GetDynamicParameters()方法或类似的东西,就像有一个可重写的BeginProcessing()方法一样。
以此速度推断,二进制模块在Powershell世界中似乎是二等公民。肯定有我错过的方法来解决这个问题吧?

1
我认为至少需要将 IDynamicParameters 接口添加到你的 cmdlet 中才能使用动态参数。从那里开始,我认为你需要像示例中一样做同样的事情(创建 RuntimeParameterDictionary 等),但是我无法确认这一点。 - Mike Zboray
IDynamicParameters 可以让您在运行时确定参数,但我认为 OP 是在询问如何提供动态参数值。 - Glenn
1个回答

11

这里有一种方法,可以在PowerShell v5中实现自定义参数补全:

Add-Type @‘
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Linq;
    using System.Management.Automation;
    using System.Management.Automation.Language;
    [Cmdlet(VerbsDiagnostic.Test,"Completion")]
    public class TestCompletionCmdlet : PSCmdlet {
        private string name;
        [Parameter,ArgumentCompleter(typeof(NameCompleter))]
        public string Name {
            set {
                name=value;
            }
        }
        protected override void BeginProcessing() {
            WriteObject(string.Format("Hello, {0}", name));
        }
        private class NameCompleter : IArgumentCompleter {
            IEnumerable<CompletionResult> IArgumentCompleter.CompleteArgument(string commandName,
                                                                              string parameterName,
                                                                              string wordToComplete,
                                                                              CommandAst commandAst,
                                                                              IDictionary fakeBoundParameters) {
                return GetAllowedNames().
                       Where(new WildcardPattern(wordToComplete+"*",WildcardOptions.IgnoreCase).IsMatch).
                       Select(s => new CompletionResult(s));
            }
            private static string[] GetAllowedNames() {
                return new string[] { "Alice", "Bob", "Charlie" };
            }
        }
    }
’@ -PassThru|Select-Object -First 1 -ExpandProperty Assembly|Import-Module

特别是,您需要:

  • 实现IArgumentCompleter接口。 实现此接口的类应具有公共默认构造函数。
  • ArgumentCompleterAttribute属性应用于用作cmdlet参数的属性或字段。 作为属性的参数,您应该传递IArgumentCompleter实现。
  • IArgumentCompleter.CompleteArgument中,您有一个wordToComplete参数,因此可以通过用户已输入的文本过滤完成选项。

并尝试它:

Test-Completion -Name Tab

我不得不安装Windows管理框架5.0预览版,但一旦安装完成,它真的可以工作!非常感谢! - Dan Stevens

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接