Как передать строку аргумента одной функции PowerShell другой? - PullRequest
12 голосов
/ 16 января 2011

Я пытаюсь написать некоторые функции PowerShell, которые выполняют некоторые функции, а затем прозрачно обращаются к существующим встроенным функциям.Я хочу передать все нетронутые аргументы.Я не хочу знать какие-либо подробности аргументов.

Я устал использовать 'splat', чтобы сделать это с @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 должна быть в состоянии обрабатывать вызовы без аргументов, с одним аргументом, с именованными аргументами, из строки, содержащей несколько полу-colon с разделителями, и с переменными в аргументах, включая строковые переменные, содержащие пробелы.По сути, это должна быть альтернатива 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
  ...

Вуаля!Эта новая версия проходит все тесты.

Ответы [ 3 ]

4 голосов
/ 16 января 2011

К сожалению, сделать обертку не так просто. Прежде всего, оператор splat предположительно должен применяться к хеш-таблице (например, автоматически PSBoundParameters или другому), а не к массиву $args напрямую.

Есть как минимум два варианта (оба не идеальны) и взлом (см. Раздел обновления).

Вариант 1. Используйте «классический» способ изготовления обертки. Пример: посмотрите, как это делается для функции help, которая является оболочкой командлета Get-Help:

Get-Content function:\help

Вы можете видеть, что функция-обертка объявляет все параметры, которые имеет упакованный командлет, чтобы 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. Но эта задача не выглядит практически привлекательной.


UPDATE

Ну, на самом деле есть еще одна специальная опция (чистый взлом!), Которая требует минимальных усилий и будет работать в некоторых тривиальных, в основном интерактивных, сценариях. Это не для какого-то серьезного кода!

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
4 голосов
/ 17 января 2011

Если вы хотите использовать обёртку для ls, вы должны написать правильную функцию прокси .На PoshCode.org есть несколько версий генератора, в том числе из Поваренной книги Ли Холмса PowerShell

2 голосов
/ 16 января 2011

Я верю этому:

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

Я бы прекратил использовать это, пока это не будет решено.

...