PowerShell – 如何用单引号括起参数自动完成的参数(包含空格)?

huangapple go评论78阅读模式
英文:

PowerShell - How to surround argumentcompleter auto-completed arguments (that contain spaces) with single quotes?

问题

我正在进行一个简单的演示,以说明在函数中使用ArgumentCompleter属性,以下是我目前正在使用的代码:

#Requires -Modules Hyper-V
#Requires -RunAsAdministrator
function Get-VMNameTest
{
    param (
        [ArgumentCompleter({
            param ($Command, $Parameter, $WordToComplete, $CommandAst, $FakeBoundParams)
            $VmNameArg = (Get-VM).Name | Where-Object {$_.Name -like "*$WordToComplete*"}
            foreach ($vm in $VmNameArg)
            {
                if ($vm -match '\s+')
                {
                    "'$vm'"
                }
                else {$vm}
            }
        })]
        [string[]]$Name
    )

    foreach ($vm in $Name)
    {
        Write-Output "VMName:  $vm"
    }
}

-Name参数将自动完成本地计算机上找到的任何VM(名称)。我添加了一个foreach循环(用*标记),以在包含空格的任何VM名称周围添加单引号 - 否则,VM名称(包含空格)将在命令行上显示时没有引号。

当使用CTRL + SPACE查看自动完成值时,本机cmdlet会在列表中呈现不带单引号的值(包含空格的值),但只要使用箭头键移动到带有空格的值上,它就会自动将单引号添加到命令行中(在这种情况下,它呈现为使用.\和尾随\的路径)。示例如下:

PS C:\> Get-Item -Path '.\Program Files\'
$Recycle.Bin               System Volume Information
Documents and Settings     Users
Program Files              Windows
Program Files (x86)
ProgramData

("Program Files" is selected via arrow keys in this example).

这是我的代码中带有foreach循环的外观 - 单引号在列表和命令行中都显示出来:

PS C:\> Get-VMNameTest -Name 'Server Two'
ServerOne                  'Server Two'

没有foreach循环,单引号不会包围带有空格的值(这显然是有问题的):

PS C:\> Get-VMNameTest -Name Server Two
ServerOne                  Server Two

我试图保持与本机cmdlet行为的一致性,所以这是我尝试实现的效果:

PS C:\> Get-VMNameTest -Name 'Server Two'
ServerOne                  Server Two

显然,本机(编译的)cmdlet的行为与添加单引号(或添加路径分隔符、转义必要字符等)的逻辑之间存在差异。虽然这只是一个轻微的外观差异,但我想知道是否有更好的方法来处理这个问题。专业人员是如何做的?

英文:

I'm doing a simple demo to illustrate the ArgumentCompleter attribute in a function, and this is the code I'm working with so far:

#Requires -Modules Hyper-V
#Requires -RunAsAdministrator
function Get-VMNameTest
{
    param (
        [ArgumentCompleter({
            param ($Command, $Parameter, $WordToComplete, $CommandAst, $FakeBoundParams)
            $VmNameArg = (Get-VM).Name | Where-Object {$_.Name -like "*$WordToComplete*"}
            *foreach ($vm in $VmNameArg)
            *{
            *    if ($vm -match '\s+')
            *    {
            *        "'" + $vm + "'"
            *    }
            *    else {$vm}
            *}
        })]
        [string[]]$Name
    )

    foreach ($vm in $Name)
    {
        Write-Output "VMName:  $vm"
    }
}

The -Name parameter will autocomplete any VM (name) that's found on the local computer. I added a foreach loop (indicated with *'s) to add single quotes around any VM names that contain spaces -- otherwise a VM name (that contains spaces) will show up without quotes on the command line.

When using CTRL + SPACE to see the autocomplete values, native cmdlets present the values (that contain spaces) without single quotes in the list, but as soon as you arrow-key over a value with spaces, it automatically adds the single quotes to the command line (in this case, it presents as a path using .\ and a trailing \. To illustrate:

PS C:\> Get-Item -Path '.\Program Files\'
$Recycle.Bin               System Volume Information
Documents and Settings     Users
Program Files              Windows
Program Files (x86)
ProgramData

("Program Files" is selected via arrow keys in this example).

<BR><BR><BR></BR>This is how my code appears with the foreach loop - the single quotes appear in both the list and the command line:

PS C:\&gt; Get-VMNameTest -Name &#39;Server Two&#39;
ServerOne                  &#39;Server Two&#39;

<BR></BR>Without the foreach loop, single quotes do not surround values with spaces (which is obviously problematic):

PS C:\&gt; Get-VMNameTest -Name Server Two
ServerOne                  Server Two

<BR></BR>I'm trying to remain consistent with the behavior of native cmdlets, so this is what I'm trying to achieve:

PS C:\&gt; Get-VMNameTest -Name &#39;Server Two&#39;
ServerOne                  Server Two

<BR><BR></BR>Clearly there's a difference in how native (compiled) cmdlets behave and the logic that adds the single quotes (or adds path separators, escapes necessary characters, etc). Although it's a minor cosmetic difference, I want to know if there's a better way to approach this. How do the pros do it?

答案1

得分: 1

如果我理解正确,您希望显示未引用的完成项,但在选择项时应该引用它,如果是这样的话,您可以使用CompletionResult(String, String, CompletionResultType, String)重载来实现:

function Test-Completion {
    param(
        [ArgumentCompleter({
            param (
                $Command,
                $Parameter,
                $WordToComplete,
                $CommandAst,
                $FakeBoundParams
            )

            # 在此处生成用于测试的项目列表,
            # 一些可以有空格,其他则没有
            $list = 0..10 | ForEach-Object {
                if($_ % 2) {
                    return "Item $_"
                }

                "Item$_"
            }

            foreach($item in $list) {
                if($item -notlike "$wordToComplete*") {
                    continue
                }

                $completionText = $item
                # 如果此项包含空格
                if($item -match '\s') {
                    $completionText = "'$item'"
                }

                [System.Management.Automation.CompletionResult]::new(
                    $completionText, $item,
                    [System.Management.Automation.CompletionResultType]::ParameterValue,
                    '如果需要的话,这里是一些工具提示')
            }
        })]
        [Parameter(Mandatory)]
        [string] $Test
    )

    $Test
}
英文:

If I understood correctly, you're looking to display completion items unquoted but when an item is selected it should be quoted, if that's the case, then you can accomplish it using the CompletionResult(String, String, CompletionResultType, String) overload:

function Test-Completion {
    param(
        [ArgumentCompleter({
            param (
                $Command,
                $Parameter,
                $WordToComplete,
                $CommandAst,
                $FakeBoundParams
            )

            # generate a list of items here for testing,
            # some can have spaces, others dont
            $list = 0..10 | ForEach-Object {
                if($_ % 2) {
                    return &quot;Item $_&quot;
                }

                &quot;Item$_&quot;
            }

            foreach($item in $list) {
                if($item -notlike &quot;$wordToComplete*&quot;) {
                    continue
                }

                $completionText = $item
                # if this item has a space
                if($item -match &#39;\s&#39;) {
                    $completionText = &quot;&#39;$item&#39;&quot;
                }

                [System.Management.Automation.CompletionResult]::new(
                    $completionText, $item,
                    [System.Management.Automation.CompletionResultType]::ParameterValue,
                    &#39;some tooltip here if you want&#39;)
            }
        })]
        [Parameter(Mandatory)]
        [string] $Test
    )

    $Test
}

huangapple
  • 本文由 发表于 2023年6月15日 07:39:45
  • 转载请务必保留本文链接:https://go.coder-hub.com/76478226.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定