将函数作为参数传递

问题描述 投票:0回答:9

我编写了函数“A”,它将调用许多其他函数之一。为了节省重写函数“A”,我想将要调用的函数作为函数“A”的参数传递。例如:

function A{
    Param($functionToCall)
    Write-Host "I'm calling : $functionToCall"
}

function B{
    Write-Host "Function B"
}

Function C{
    write-host "Function C"
}

A -functionToCall C

返回:我打电话:C

我期待它返回:我正在调用:函数 C。

我尝试过各种方法,例如:

Param([scriptblock]$functionToCall)

无法将 System.String 转换为 ScriptBlock

A -functionToCall $function:C

返回“写主机”功能C”

A - functionToCall (&C)

这会在其余部分之前进行评估:

 Function C
 I'm Calling :

我确定这是编程 101,但我无法弄清楚正确的语法或我做错了什么。

powershell parameter-passing calling-convention
9个回答
64
投票

我不确定这是最好的,但是:

function A{
    Param([scriptblock]$FunctionToCall)
    Write-Host "I'm calling $($FunctionToCall.Invoke(4))"
}

function B($x){
    Write-Output "Function B with $x"
}

Function C{
    Param($x)
    Write-Output "Function C with $x"
}

PS C:\WINDOWS\system32> A -FunctionToCall $function:B
I'm calling Function B with 4

PS C:\WINDOWS\system32> A -FunctionToCall $function:C
I'm calling Function C with 4

PS C:\WINDOWS\system32> A -FunctionToCall { Param($x) "Got $x" }
I'm calling Got x

17
投票

您是否考虑过传递 ScriptBlock 作为参数?

$scriptBlock = { Write-Host "This is a script block" }
Function f([ScriptBlock]$s) {
  Write-Host "Invoking ScriptBlock: "
  $s.Invoke()
}

PS C:\> f $scriptBlock
Invoking ScriptBlock:
This is a script block

17
投票

如果您确实想将函数的 name 作为 string 传递:使用

&
调用运算符)来调用它:

function A {
  Param($functionToCall)
  # Note the need to enclose a command embedded in a string in $(...)
  Write-Host "I'm calling: $(& $functionToCall)"
}

Function C {
  "Function C"  # Note: Do NOT use Write-Host to output *data*.
}

A -functionToCall C

至于

$(...)
内部是否需要使用
"..."
:请参阅这个答案,它解释了PowerShell的字符串扩展(字符串插值)规则。

以上产量

I'm calling: Function C

注意函数

C
如何使用隐式输出(与显式使用
Write-Output
相同)来返回值。
Write-Host
通常是错误使用的工具
,除非意图明确仅写入显示,绕过 PowerShell 的输出流。

您通常在以下场景中需要

&
运算符:

  • 通过变量引用和/或名称是单引号或双引号来按名称或路径调用命令。

  • 调用脚本块

脚本块是在PowerShell中传递代码片段的首选方式;上面的内容可以重写为(注意调用机制没有改变,只是传递了参数):

function A {
  Param($scriptBlockToCall)
  Write-Host "I'm calling: $(& $scriptBlockToCall)"
}

Function C {
  "Function C"  # Note: Do NOT use Write-Host to output *data*.
}

A -scriptBlockToCall { C }

在任何一种情况下,要传递参数,只需将它们放在:

& <commandNameOrScriptBlock>
之后;请注意如何使用 splatting (
@<var>
) 来传递存储在 automatic
$args
变量
中的未绑定参数。

function A {
  Param($commandNameOrScriptBlockToCall)
  Write-Host "I'm calling: $(& $commandNameOrScriptBlockToCall @Args)"
}

Function C {
  "Function C with args: $Args"
}


A -commandNameOrScriptBlockToCall C one two # by name
A -commandNameOrScriptBlockToCall { C @Args } one two # by script block

以上产量

I'm calling: Function C with args: one two
两倍。

注意

  • 正如 JohnLBevan 指出的那样,自动

    $args
    变量仅在 simple(非高级)脚本和函数中可用。

  • [CmdletBinding()]
    块上方使用
    param(...)
    属性和/或每个参数
    [Parameter()]
    属性使脚本或函数成为 高级 ,并且高级脚本和函数另外仅接受以下参数:绑定到显式声明参数。

  • 如果您需要使用高级脚本或函数 - 例如使用

    [CmdletBinding(SupportsShouldProcess)]
    支持假设功能 - 您可以选择以下方式传递参数:

    • 如果足以传递 positional(未命名)参数,请声明一个参数,例如

      [Parameter(ValueFromRemainingArguments)] $PassThruArgs
      ,它隐式收集调用时传递的所有位置参数。

    • 否则,您必须显式声明所有潜在(命名)传递参数的参数。

      • 您可以借助 PowerShell SDK(一种用于创建代理(包装器)函数的技术)基于现有命令构建参数声明,如此答案所示。
    • 或者,您的函数可以声明一个参数,该参数接受表示命名传递参数的 hashtable,以便与 splatting 一起使用;当然,这需要调用者显式构造这样的哈希表。


9
投票

这是您需要的吗?

function A{
    Param($functionToCall)
    Write-Host "I'm calling : $functionToCall"

    #access the function-object like this.. Ex. get the value of the StartPosition property
    (Get-Item "function:$functionToCall").ScriptBlock.StartPosition

}

function B{
    Write-Host "Function B"
}

Function C{
    write-host "Function C"
}


PS> a -functionToCall c

I'm calling : c


Content     : Function C{
                  write-host "Function C"
              }
Type        : Position
Start       : 307
Length      : 43
StartLine   : 14
StartColumn : 1
EndLine     : 16
EndColumn   : 2

5
投票

邓肯的解决方案对我来说非常有效。但是,当函数名称中包含破折号时,我遇到了一些问题。

我能够通过构建他的第三个示例来解决这个问题:

function A{
    Param([scriptblock]$functionToCall)
    Write-Host "I'm calling $($functionToCall.Invoke(4))"
}

function Execute-FunctionWithDash($x)
{
    Write-Output "Function Execute-FunctionWithDash with $x"
}

PS C:\WINDOWS\system32> A -functionToCall { Param($x) Execute-FunctionWithDash $x }
I'm calling Function Execute-FunctionWithDash with 4

2
投票

用于传递可变数量的命名参数

function L($Lambda){
   write-host "`nI'm calling $Lambda"
   write-host "`nWith parameters"; ft -InputObject $Args
   & $Lambda @Args
}

似乎可以很好地处理奇怪的函数名称

function +Strange-Name($NotUsed,$Named1,$Named2){
   ls -filter $Named1 -Attributes $Named2
}

PS C:\>L +Strange-Name -Named1 *.txt -Named2 Archive

还有exe文件

PS C:\>L grep.exe ".*some text.*" *.txt

虽然看起来还是要注意注射

function inject($OrigFunction){
   write-host 'pre-run injection'
   & $OrigFunction @Args
   write-host 'post-run injection'
}

PS C:\>L inject +Strange-Name -Named1 *.txt -Named2 Archive

1
投票
    function strdel($a,$b,$c) {
    return ($a.substring(0,$b)+$(substr $a $c $a.length))
}
function substr($a,$b,$c) {
    return $a.substring($b,($c-$b))
}

$string = "Bark in the woods"
$in = $(substr $(strdel $string 0 5) 0 2)
write-host $in

函数“substr”调用函数“strdel”作为 $a 参数。

功能来自https://github.com/brandoncomputer/vds


1
投票

怎么样:

function A{
Param($functionToCall)
    $res = Invoke-Command $functionToCall 
    Write-Host "I'm calling : $res"
}

function B{
    "Function B"
}

Function C{
    "Function C"
}

A -functionToCall ${function:C}

使用 ${function:...} 将函数作为值路径。 调用该函数并将结果保存到 $res。


0
投票

变量传递给函数,同时传递某些变量的(当前值)作为参数

function MyPrint($x,$y) { Write-Host "$x $y"}
$txt="Hi"; $num=101

# 2-stage definition of anon function (don't ask why...)
$_f = {param($x,$y) MyPrint $x $y}

# "copy" existing vars into new scope
$f  = {& $_f $txt $num}.GetNewClosure()

$f.Invoke()            # "Hi 101"
$txt="Nooo"; $num=777
$f.Invoke()            # Still "Hi 101"

MyButton.Add_Click($f) # Use in callbacks
© www.soinside.com 2019 - 2024. All rights reserved.