如何将一个PowerShell函数的'argument-line'传递给另一个?

jwf*_*arn 12 powershell metaprogramming splat

我正在尝试编写一些PowerShell函数来执行某些操作,然后透明地调用现有的内置函数.我想传递所有未触及的论点.我不想知道论点的任何细节.

我厌倦了使用'splat'来做这件事@args但是没有按照我的预期工作.

在下面的例子中,我写了一个叫做myls打印你好的玩具函数!然后调用相同的内置函数,Get-ChildItem内置别名ls调用其余参数行完整.到目前为止我的工作得很好:

function myls
{
  Write-Output "hello!"
# $MyInvocation | Format-List          # <-- uncomment this line for debug info
  Invoke-Expression ("Get-ChildItem " + $MyInvocation.UnboundArguments -join " ")
}
Run Code Online (Sandbox Code Playgroud)

正确的版本myls应该能够处理没有参数的调用,带有一个带有命名参数的参数,来自包含多个分号分隔命令的行,并且参数中的变量包括包含空格的字符串变量.基本上,它应该是替代品ls.

下面的测试比较myls和内置ls:

[注意:输出省略和/或压缩以节省空间]

PS> md C:\p\d\x, C:\p\d\y, C:\p\d\"jay z"
PS> cd C:\p\d
PS> ls                                 # no args
PS> myls                               # pass
PS> cd ..
PS> ls d                               # one arg
PS> myls d                             # pass
PS> $a="A"; $z="Z"; $y="y"; $jz="jay z"
PS> $a; ls d; $z                       # multiple statements
PS> $a; myls d; $z                     # pass
PS> $a; ls d -Exclude x; $z            # named args
PS> $a; myls d -Exclude x; $z          # pass
PS> $a; ls d -Exclude $y; $z           # variables in arg-line
PS> $a; myls d -Exclude $y; $z         # pass
PS> $a; ls d -Exclude $jz; $z          # variables containing spaces in arg-line
PS> $a; myls d -Exclude $jz; $z        # FAIL!
Run Code Online (Sandbox Code Playgroud)

有没有办法可以重新编写myls以获得我想要的行为?

简短的回答:是的,这是可能的.坏消息:它需要代码知道参数的细节以及有关希望调用的函数的其他元数据.好消息:一个人不需要自己写这个.这个元数据以编程方式提供,并且存在可用的模块,可用于自动生成骨架代理代码(请参阅下面的@ Jaykul的答案).我选择使用名为"MetaProgramming"的模块.导入后,生成一个myls简单的脚本很简单:

New-ProxyCommand ls > .\myls.ps1

然后可以开始自定义新生成的myls.ps1脚本,如下所示:

  ...
  begin
  {
    Write-Output "hello!"              # <-- add this line
    try {
      $outBuffer = $null
  ...
Run Code Online (Sandbox Code Playgroud)

瞧!这个新版本通过了所有测试.

Jay*_*kul 4

如果您想要 ls 的直接包装器,您应该编写一个正确的 Proxy Command。PoshCode.org 上有几个版本的生成器,包括Lee Holmes 的 PowerShell Cookbook 中的版本

但是现在内置了代理命令生成器,因此您可以编写:

$CommandName = "Get-ChildItem"
$Command = Get-Command $CommandName
[System.Management.Automation.ProxyCommand]::Create($Command)
Run Code Online (Sandbox Code Playgroud)

  • 如果您可以复制粘贴,则不需要代码生成。OP正在做的是尝试对命令进行子类化:根据原始命令编写一个新命令,但进行自定义修改......并在脚本中。如果您在 C# 中执行此操作,则只需继承即可。但是您使用的是 PowerShell,因此您必须编写一个函数,该函数首先复制原始 cmdlet 中的参数集(在 PowerShell 中,我们称之为代理函数)。如果有什么东西需要代码生成,那就是它了。 (2认同)