繁体   English   中英

如何将一个 PowerShell 函数的“参数行”传递给另一个函数?

[英]How to pass the 'argument-line' of one PowerShell function to another?

我正在尝试编写一些 PowerShell 函数来做一些事情,然后透明地调用现有的内置函数。 我想原封不动地传递所有论点。 我不想知道论点的任何细节。

我厌倦了使用 'splat' 来用@args来做这@args但这并没有像我预期的那样工作。

在下面的示例中,我编写了一个名为myls的玩具函数,它应该打印hello! 然后调用相同的内置函数Get-ChildItem ,内置别名ls在参数行的其余部分完好无损的情况下调用该函数。 到目前为止我所拥有的效果很好:

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

正确版本的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!

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

简短回答:是的,这是可能的。 坏消息是:它需要知道参数的详细信息和关于希望调用的函数的其他元数据的代码。 好消息:你不需要自己写这一切 此元数据可通过编程方式获得,并且存在可用于自动生成骨架代理代码的模块(请参阅下面的@Jaykul 的回答)。 我选择使用名为 "MetaProgramming" 的模块 导入后,生成插入式myls脚本非常简单:

New-ProxyCommand ls > .\\myls.ps1

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

  ...
  begin
  {
    Write-Output "hello!"              # <-- add this line
    try {
      $outBuffer = $null
  ...

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

不幸的是,正确制作包装纸并不容易。 首先,splat 运算符大概应该应用于哈希表(例如自动PSBoundParameters或其他),而不是直接应用于数组$args

至少有两个选项(都不完美)和一个 hack(参见更新部分)。

选项 1. 使用“经典”方式制作包装纸。 示例:查看函数help是如何完成的,它是Get-Help cmdlet 的包装器:

Get-Content function:\help

您可以看到包装函数声明了被包装 cmdlet 具有的所有参数,以便将PSBoundParameters绑定到现有函数参数

你的例子。 如果您的函数声明了参数Exclude则示例代码开始工作。 但它仅适用于Exclude ,不适用于Force ,尽管Force也传入:

function myls($Exclude) {
    # only Exclude is in $PSBoundParameters even though we send Force, too:
    $PSBoundParameters | Out-String | Out-Host
    Write-Output "hello!"
    Get-ChildItem @PSBoundParameters
}
cd d
myls -Exclude b -Force

选项 2。理论上应该可以从数组$args手动构建一个哈希表并将 splat 运算符应用于它。 但这项任务实际上看起来并不吸引人。


更新

嗯,实际上还有另一个特别的选项(纯粹的 hack!),它需要最少的努力,并且可以在一些琐碎的、主要是交互式的场景中工作。 它不是为了某种严肃的代码!

function myls {
    # extra job
    Write-Output "hello!"

    # invoke/repeat the calling code line with myls "replaced" with Get-ChildItem
    Set-Alias myls Get-ChildItem
    Invoke-Expression $MyInvocation.Line
}

cd d

# variables can be used as the parameter values, too
$exclude = 'b'

myls -Exclude $exclude -Force

如果您想要 ls 的嵌入式包装器,您应该编写一个适当的 Proxy Command PoshCode.org 上有几个版本的生成器,包括Lee Holmes 的 PowerShell Cookbook 中的一个

但是代理命令生成器现在是内置的,所以你可以只写:

$CommandName = "Get-ChildItem"
$Command = Get-Command $CommandName
[System.Management.Automation.ProxyCommand]::Create($Command)

我相信这一点:

函数 myls { 写输出“你好!”; iex "Get-ChildItem @args"}

将更接近产生预期的结果。

更新:以这种方式使用@args 传递命名参数显然存在一个已知错误:

https://connect.microsoft.com/PowerShell/feedback/details/368512/splat-operator-args-doesnt-properly-pass-named-arguments?wa=wsignin1.0

我会停止使用它,直到它得到解决。

暂无
暂无

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

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