有没有一种方法可以创建 cmdlet 的别名,使其仅在将参数传递给别名时运行?

Is there a way to create an alias to a cmdlet in a way that it only runs if arguments are passed to the alias?

我正在尝试 create an alias (named which) of the Get-Command cmdlet,如果我没有发送任何参数,它不会 运行(因为如果它 运行 没有参数,它会输出所有可用的命令)。

我知道这可以使用一个函数来完成,但我想保留制表符完成功能,而不必编写一个要放入我的 $PROFILE.

中的相当大的函数

简而言之,我只希望别名在传递参数时起作用。

你不能用别名,因为PowerShell 别名只能引用另一个命令名称或路径[=145] =],因此既不能包含 参数 也不能包含自定义逻辑 .

因此你确实需要一个函数,但它可以是简短一个:

function which { if ($args.count) { Get-Command @args } else { Throw "Missing command name." } }

请注意,虽然传递 -? 以显示 Get-Command 的帮助确实有效,但参数的制表符补全无效。

为了也完成制表符,您需要编写一个包装器(代理)函数或至少复制Get-Command的参数声明 - 然后确实 使函数定义相当大。

如果只关心 $PROFILE 文件本身的大小,您可以编写一个代理 script - which.ps1 - 您可以调用它也只有 which,假设您将它放在 $env:Path[1] 中列出的目录之一;请参阅下一节。


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

Defining a wrapper (proxy) function or script 是一项不平凡的工作,但允许您实现强大的包装器,支持制表符完成,甚至转发到原始命令的帮助。

注:

  • 错误警报:作为 zett42 points out, as of PowerShell [Core] 7.1, System.Management.Automation.ProxyCommand.Create neglects to include dynamic parameters if the target command is an (advanced) function or script; however, compiled cmdlets are not affected; see GitHub issue #4792 and 解决方法。

  • 为简单起见,下面创建一个包装器 script, which.ps1 ,并将其保存在当前目录中。如前所述,如果您将它放在 $env:PATH 中列出的目录之一中,您将能够像 which.

    一样调用它
  • 下面的代码可以很容易地修改为创建一个包装器 function:只需获取下面的 $wrapperCmdSource 变量的内容并将其括起来在 function which { ... }.

  • 从 PowerShell Core 7.0.0-preview.5 开始,自动生成的代码存在一些问题,这些问题可能会影响您,也可能不会;他们会在某个时候被修复;要了解更多信息以及如何手动更正它们,请参阅 GitHub issue #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

注:

  • 尽管 Get-Command 输出的 System.Management.Automation.ProxyCommand.Create requires a System.Management.Automation.CommandMetadata instance to identify the target command, the System.Management.Automation.CommandInfo 个实例可以按原样使用。

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

    • 通过结合使用 [System.Management.Automation.ProxyCommand]::GetHelpComments()Get-Help 的输出,您可以从原始命令帮助的副本开始并修改它: [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.
...

其他常见的自定义类型是:

  • 通过简单地删除参数声明从包装器中删除参数。

  • 通过修改 begin 块中的以下行,将附加参数添加到包装命令的调用中:

      # Add parameters, as needed.
      $scriptCmd = { & $wrappedCmd @PSBoundParameters } 
    
  • 在将管道输入传递给包装命令之前对其进行预处理,方法是自定义 process 块并在以下行中将 $_ 替换为您的预处理输入:

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

[1] Linux 用户注意:因为 Linux 文件系统是 case-sensitive,您的脚本调用将无法工作 insensitively,命令在 PowerShell 中的正常工作方式。例如,如果您的脚本文件名为 Get-Foo.ps1,则只有 Get-Foo - 使用完全相同的大小写 - 会起作用,而不是 get-foo,例如