当函数没有return语句时,ScriptBlock中带有本地函数的Invoke-Command不起作用

时间:2015-07-16 08:03:23

标签: powershell

我试图通过传递本地定义的函数来使Invoke-Command cmdlet使用CredSsp会话。

我有两个本地功能:

  • MyCopy只在带有附加开关的传递参数上调用Copy-Item cmdlet
  • MyCopyReturn与上述相同,但最后还有return声明

现在,当我将这些函数传递给Invoke-Command而没有-Session参数时,它们都成功了。 但是,为-Session指定Invoke-Command时,MyCopy会失败并出现以下异常(2.案例):

Cannot bind argument to parameter 'Path' because it is null.
    + CategoryInfo          : InvalidData: (:) [Copy-Item], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.CopyItemCommand
    + PSComputerName        : localhost

有人可以解释为什么传递MyCopyReturn的结果与传递MyCopy不同?

function MyCopy ($from, $to) {
    Copy-Item $from $to -Force -Recurse -Verbose
}

function MyCopyReturn ($from, $to) {
    Copy-Item $from $to -Force -Recurse -Verbose
    return
}

$credential = Get-Credential
$computerName = 'localhost'
$session = New-PSSession -ComputerName $computerName -Credential $credential -Authentication Credssp
# src is a file
$src = "c:\sandbox\a\test"
# dest is a directory
$dest = "c:\sandbox\b"

# 1.
# MyCopy works fine without giving session
Invoke-Command -ScriptBlock ${function:MyCopy} -ArgumentList $src,$dest

# 2.
# MyCopy DOESN'T WORK when session is given
Invoke-Command -Session $session -ScriptBlock ${function:MyCopy} -ArgumentList $src,$dest

# 4.
# MyCopyReturn works fine without the session
Invoke-Command -ScriptBlock ${function:MyCopyReturn} -ArgumentList $src,$dest

# 3.
# MyCopyReturn works fine with session too
Invoke-Command -Session $session -ScriptBlock ${function:MyCopyReturn} -ArgumentList $src,$dest

Remove-PSSession $session

1 个答案:

答案 0 :(得分:1)

我用ILSpy进行了一些挖掘,可以说ScriptBlock.GetPowerShell方法中的bug存在问题。如果参数不是来自param块:

,它不会正确绑定参数
function f($Arg){write "`$Arg=`"$Arg`";`$Args=`"$Args`""}
function g{param($Arg) write "`$Arg=`"$Arg`";`$Args=`"$Args`""}

$Function:f.GetPowerShell('Test1','Test2').Invoke()
# $Arg="";$Args="Test1 Test2"
$Function:g.GetPowerShell('Test1','Test2').Invoke()
# $Arg="Test1";$Args="Test2"

如您所见,$Function:f.GetPowerShell('Test1','Test2')将两个参数绑定到$Args自动变量,并且没有任何内容绑定到$Arg。所以当你调用这个命令时

Invoke-Command -Session $session -ScriptBlock ${function:MyCopy} -ArgumentList $src,$dest

$from$to未正确绑定并导致错误。

为什么不使用-Session参数时不会发生错误?
看起来Invoke-Command必须将ScriptBlock转换为PowerShell以在远程会话中调用命令,而在本地调用ScriptBlock时则不需要。

为什么MyCopyReturn不会导致同样的错误?
并非所有ScripBlock都可以通过GetPowerShell方法转换为PowerShell(特别是,ScriptBlock必须有一个语句,另一个语句return违反此条款) 。如果是这种情况(GetPowerShell抛出ScriptBlockToPowerShellNotSupportedException),则Invoke-Command使用某些备份方案将ScriptBlock转换为PowerShell,并且该方案不会受到错误的影响GetPowerShell方法。