【问题标题】:Pass function as a parameter将函数作为参数传递
【发布时间】:2023-04-02 20:56:01
【问题描述】:

我编写了函数“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


    【解决方案1】:

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

    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
    

    【讨论】:

    • “4”代表什么,如.Invoke(4)?谢谢。
    • 只是证明你可以在调用函数时传递参数
    • 你不能把它适当地包装起来以解决破折号吗? A -functionToCall ${Function:Execute-FunctionWithDash}
    • 如果要使用脚本块的结果,可能会考虑使用 InvokeResultAsIs 而不是 Invoke,因为 Invoke 似乎返回一个集合 - 即使只返回一个对象。
    【解决方案2】:

    您是否考虑过将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
    

    【讨论】:

      【解决方案3】:

      如果你真的想将函数的名称作为字符串传递:使用&调用运算符,调用它:

      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
      

      至于"..."里面需要使用$(...):见this answer,里面解释了PowerShell的字符串扩展(string-interpolation)规则。

      以上产生I'm calling: Function C

      注意函数C 如何使用隐式 输出(与显式使用Write-Output 相同)返回值。
      Write-Host is generally the wrong tool to use,除非意图明确写入显示器仅,绕过 PowerShell 的输出流。

      您一般在以下场景中需要& 运算符:

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

      • 调用一个脚本块

      Script blocks 是在 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 variable 中的未绑定参数的。

      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 变量仅在简单(非高级)脚本和函数中可用。

      • param(...) 块上方使用[CmdletBinding()] 属性和/或每个参数的[Parameter()] 属性使脚本或函数成为advanced 之一,并且高级脚本和函数还只接受参数绑定到显式声明的参数。

      • 如果您需要使用高级脚本或函数 - 例如通过 [CmdletBinding(SupportsShouldProcess)] 支持假设功能 - 您有以下选择来传递参数:

        • 如果传递位置(未命名)参数就足够了,请声明一个参数,例如[Parameter(ValueFromRemainingArguments)] $PassThruArgs,它会隐式收集调用时传递的所有位置参数。

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

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

      【讨论】:

        【解决方案4】:

        这是你需要的吗?

        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
        

        【讨论】:

        • 不,那只是将字符串“c”传递给函数,而不是将函数对象C作为参数传递。
        • 问题不清楚。查看更新的答案。使用 scriptblock-property 你也可以调用传递函数
        • 正确。非常感谢@Frode F。非常感谢。当您知道如何操作时,如此简单。
        • 它仍在传递函数的名称,然后从 A 内部的函数命名空间中查找它。这可能会起作用,但它会阻止您在调用点内联定义函数.
        • (Get-Item "function:$functionToCall").ScriptBlock.InvokeWithContext($null,$null) 工作;或invoke-command (Get-Item "function:$functionToCall").ScriptBlock
        【解决方案5】:

        Duncan 的解决方案非常适合我。但是,当函数名称中有破折号时,我遇到了一些问题。

        我能够通过构建他的第三个示例来绕过它:

        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
        

        【讨论】:

        • 这样你传递的是 $x 但不是不受欢迎的函数。
        • 其实最后一行是调用函数A,传入一个带参数$x的函数,调用Execute-FunctionWithDash,传递参数$x给它。
        • 带破折号的函数可以这样访问:${Function:Do-SomethingCool}
        【解决方案6】:

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

        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
        

        【讨论】:

          【解决方案7】:
              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的函数

          【讨论】:

            【解决方案8】:

            怎么样:

            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。

            【讨论】:

              猜你喜欢
              • 2021-04-13
              • 2013-01-27
              • 1970-01-01
              • 2018-10-26
              • 2011-08-06
              • 2012-10-28
              • 2012-11-19
              相关资源
              最近更新 更多