简体   繁体   English

Powershell二进制模块:Cmdlet参数值的动态选项卡完成

[英]Powershell binary module: Dynamic tab completion for Cmdlet parameter values

I'm writing a binary Powershell module in C# and I'd like to have a Cmdlet with a parameter that provides dynamic, run-time tab completion. 我正在用C#编写一个二进制Powershell模块,我想要一个带有参数的Cmdlet,它提供动态的运行时选项卡完成。 However, I'm struggling to figure out how to do this in a binary module. 但是,我正在努力弄清楚如何在二进制模块中执行此操作。 Here's my attempt to get this working: 这是我试图让这个工作:

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

This doesn't work. 这不起作用。 I don't get any tab completion functionality. 我没有任何标签完成功能。 I also get an error: 我也收到一个错误:

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

I've found an article with an example of how to do it in a non-binary Powershell module. 我发现了一篇文章,其中有一个如何在非二进制Powershell模块中执行此操作的示例。 It seems in non-binary modules you include DynamicParam followed by statements that build and return a RuntimeParameterDictionary object. 在非二进制模块中,您似乎包含DynamicParam后跟构建和返回RuntimeParameterDictionary对象的语句。 Based on this examplem I would at expect the equivalent in the PSCmdlet class, perhaps an overridable GetDynamicParameters() method or something similiar, just as there is an overridable BeginProcessing() method. 基于这个例子,我会期望PSCmdlet类中的等价物,可能是一个可GetDynamicParameters()方法或类似的东西,就像有一个可覆盖的BeginProcessing()方法一样。

At this rate, binary modules are looking to be second-class citizens in the Powershell world. 按照这个速度,二元模块正在成为Powershell世界中的二等公民。 Surely there is a way to do this that I've missed? 当然有一种方法可以做到这一点我错过了吗?

Here is a way how you can implement custom argument completer in PowerShell v5: 以下是在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

In particular, you need: 特别是,您需要:

  • Implement IArgumentCompleter interface. 实现IArgumentCompleter接口。 Class implementing this interface should have public default constructor. 实现此接口的类应具有公共默认构造函数。
  • Apply ArgumentCompleterAttribute attribute to property of field, used as cmdlet parameter. ArgumentCompleterAttribute属性应用于字段的属性,用作cmdlet参数。 As parameter to attribute, you should pass IArgumentCompleter implementation. 作为属性的参数,您应该传递IArgumentCompleter实现。
  • In IArgumentCompleter.CompleteArgument you have wordToComplete parameter, so you can filter completion options by text, already inputted by user. IArgumentCompleter.CompleteArgument您有wordToComplete参数,因此您可以按用户输入的文本过滤完成选项。

And to try it: 尝试一下:

Test-Completion -Name Tab

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM