簡體   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