有没有一种方法可以创建一个别名到cmdlet,使它仅在参数被传递给别名时运行?

7
我正在尝试创建一个别名(名为which)的Get-Command cmdlet,以使它在没有发送任何参数的情况下不运行(因为如果没有参数运行它会输出所有可用的命令)。
我知道这可以使用函数完成,但我想保留标签自动完成功能,而不必编写一个需要放置到$PROFILE中的大型函数。
简而言之,我只希望在传递参数时才能使用该别名。
1个回答

10
你无法使用别名来完成此操作,因为PowerShell别名只能引用另一个命令的名称或路径,因此既不能包括参数也不能自定义逻辑。
因此,你确实需要一个函数,但它可以是简短和简单的。
function which { if ($args.count) { Get-Command @args } else { Throw "Missing command name." } }

请注意,虽然传递-?以显示Get-Command的帮助确实有效,但参数的制表符自动完成却不起作用。
为了获得制表符自动完成,您需要编写一个包装(代理)函数或至少复制Get-Command的参数声明 - 然后函数定义就会变得相当大。
如果问题只是$PROFILE文件本身的大小,那么您可以编写代理脚本 - which.ps1 - 您也可以使用which来调用它,假设您将其放置在$env:Path[1]中列出的目录之一中;请参见下一节。

定义一个包装(代理)脚本或函数:

定义一个包装(代理)函数或脚本 是一项不容易的任务,但可以让您实现一个强大的包装器,支持选项补全甚至转发到原始命令的帮助文档。

注意:

  • 漏洞警报:正如zett42所指出的那样,从PowerShell [Core] 7.1开始,如果目标命令是(高级)函数或脚本,则System.Management.Automation.ProxyCommand.Create会忽略包括动态参数;然而,编译的cmdlets不受影响;请参见GitHub问题#4792this答案以获取解决方法。

  • 为了简单起见,以下代码创建了一个包装器脚本which.ps1,并将其保存在当前目录中。如上所述,如果您将其放置在$env:PATH列出的目录之一中,则可以仅使用which调用它。

  • 下面的代码可以很容易地适应为创建一个包装器函数:只需取下面的$wrapperCmdSource变量的内容,并将其包含在function which { ... }中即可。

  • 截至PowerShell Core 7.0.0-preview.5,自动生成的代码存在一些问题,这可能会影响您,也可能不会;它们将在某个时候得到修复;要了解更多信息并学习如何手动纠正它们,请参见GitHub问题#10863

# Create the wrapper scaffolding as source code (outputs a single [string])
$wrapperCmdSource = 
  [System.Management.Automation.ProxyCommand]::Create((Get-Command Get-Command))
  
# Write the auto-generated source code to a script file
$wrapperCmdSource > which.ps1

注意:
  • 尽管System.Management.Automation.ProxyCommand.Create需要一个System.Management.Automation.CommandMetadata实例来标识目标命令,但由Get-Command输出的System.Management.Automation.CommandInfo实例可以直接使用。

  • 关于基于注释的帮助:默认情况下,代理函数只是简单地转发到原始 cmdlet 的帮助;但是,您可以选择将字符串作为第二个参数传递以用作基于注释的帮助。

    • 通过在Get-Help的输出中结合使用[System.Management.Automation.ProxyCommand]::GetHelpComments(),您可以从原始命令的帮助副本开始进行修改: [System.Management.Automation.ProxyCommand]::GetHelpComments((Get-Help Get-Command))

现在您拥有一个完全功能的 which.ps1 包装脚本,其行为类似于 Get-Command

您可以按以下方式调用它:

./which    # Same as: Get-Command; tab completion of parameters supported.
./which -? # Shows Get-Command's help.

你现在可以编辑脚本文件来执行所需的自定义操作。
注意:自动生成的源代码包含大量样板代码;然而,通常只需要调整一两个地方即可实现自定义功能。
具体来说,在begin { ... }块内将以下命令放置为第一条语句:
if (-not $MyInvocation.ExpectingInput -and -not ($Name -or $CommandType -or $Module -or $FullyQualifiedModule)) {
  Throw "Missing command name or filter."
}

这会导致脚本抛出错误,如果调用者没有提供直接参数或通过管道指定特定命令或一组命令的方式,则无法定位。
如果现在不带参数调用修改后的脚本,您应该会看到所需的错误:
PS> ./which.ps1
Missing command name or filter.
...

其他常见的自定义类型包括:

  • Removing parameters from the wrapper, by simply removing the parameter declaration.

  • Adding additional parameters to the invocation of the wrapped command, by modifying the following line in the begin block:

      # Add parameters, as needed.
      $scriptCmd = { & $wrappedCmd @PSBoundParameters } 
    
  • Preprocessing pipeline input before passing it to the wrapped command, by customizing the process block and replacing $_ with your preprocessed input in the following line:

      # Replace $_ with a preprocessed version of it, as needed.
      $steppablePipeline.Process($_)
    

如果你需要一个完整实现的代理函数示例,可以参考this answer


[1] 注意事项:对于Linux用户来说,由于Linux文件系统是大小写敏感的,所以调用您的脚本时不会像在PowerShell中通常工作的命令一样忽略大小写。例如,如果您的脚本文件名为Get-Foo.ps1,只有使用完全相同的大小写Get-Foo才能起作用,而不是像get-foo这样的小写。


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