13

私はいくつかのことを行い、既存の組み込み関数を透過的に呼び出すいくつかの PowerShell 関数を作成しようとしています。すべての議論を手付かずのまま伝えたいと思います。引数の詳細を知る必要はありません。

これを行うために「splat」を使用するのにうんざりしまし@argsたが、期待どおりに機能しませんでした。

以下の例では、 hello!mylsを出力するというおもちゃの関数を作成しました。次に、ビルトイン エイリアスが引数行の残りの部分を変更せずに呼び出す同じビルトイン関数 を呼び出します。私がこれまでに持っているものはかなりうまくいきます:Get-ChildItemls

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

の正しいバージョンはmyls、引数なし、1 つの引数、名前付き引数、セミコロンで区切られた複数のコマンドを含む行からの呼び出し、およびスペースを含む文字列変数を含む引数内の変数を使用した呼び出しを処理できる必要があります。基本的には、ドロップインの代わりになるはず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
  ...

出来上がり!この新しいバージョンはすべてのテストに合格しています。

4

3 に答える 3

4

ls のドロップイン ラッパーが必要な場合は、適切な Proxy Commandを記述する必要があります。PoshCode.org には、Lee Holmes の PowerShell Cookbookのバージョンを含む、いくつかのバージョンのジェネレーターがあります。

しかし、現在はプロキシ コマンド ジェネレーターが組み込まれているため、次のように記述できます。

$CommandName = "Get-ChildItem"
$Command = Get-Command $CommandName
[System.Management.Automation.ProxyCommand]::Create($Command)
于 2011-01-17T03:46:46.247 に答える
4

Making a wrapper properly is not that easy, unfortunately. First of all, the splat operator should presumably be applied to a hashtable (e.g. automatic PSBoundParameters or another), not to the array $args directly.

There are at least two options (both are not perfect), and a hack (see the update section).

Option 1. Use the "classic" way of making a wrapper. Example: see how this is done for the function help which is a wrapper of the Get-Help cmdlet:

Get-Content function:\help

You can see that the wrapper function declarers all the parameters that the wrapped cmdlet has in order to have that PSBoundParameters bounded to the existing function parameters.

Your example. If your function declares the parameter Exclude then the example code starts to work. But it works for Exclude only, not Force, though Force is also passed in:

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

Option 2. In theory in should be possible to build a hashtable from the array $args manually and apply the splat operator to it. But this task does not look practically attractive.


UPDATE

Well, there is actually yet another ad-hoc option (pure hack!) that requires minimum effort and will work in some trivial, mostly interactive, scenarios. It is not for somehow serious code!

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
于 2011-01-16T09:58:33.490 に答える
2

私はこれを信じています:

function myls { Write-Output "hello!"; iex "Get-ChildItem @args"}

期待通りの結果に近づきます。

更新: この方法で @args を使用して名前付きパラメーターを渡すことには、明らかに既知のバグがあります。

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

解決するまで使用を中止します。

于 2011-01-16T05:26:20.017 に答える