【问题标题】:PowerShell Splatting the Argumentlist on Invoke-CommandPowerShell 在 Invoke-Command 上喷出参数列表
【发布时间】:2015-03-29 20:20:04
【问题描述】:

如何将哈希表中收集的参数用于Invoke-Command 上的ArgumentList

$CopyParams = @{
    Source      = 'E:\DEPARTMENTS\CBR\SHARE\Target'
    Destination = 'E:\DEPARTMENTS\CBR\SHARE\Target 2'
    Structure   = 'yyyy-MM-dd'
}
Invoke-Command -Credential $Cred -ComputerName 'SERVER' -ScriptBlock ${Function:Copy-FilesHC} -ArgumentList @CopyParams

无论我尝试什么,它总是抱怨“来源”:

Cannot validate argument on parameter 'Source'. The "Test-Path $_" validation script for the argument with
 value "System.Collections.Hashtable" did not return true. Determine why the validation script failed

这个blog 谈到了一个类似的问题,但我无法让它工作。

对于Invoke-Command 中的简单Copy-Item 也是如此,例如:

Invoke-Command -Credential $Cred -ComputerName 'SERVER' -ScriptBlock {Copy-Item} -ArgumentList @CopyParams

Invoke-Command : Missing an argument for parameter 'ArgumentList'. Specify a parameter of type 'System.Obj
ect[]' and try again.
At line:11 char:89
+ ... ck {Copy-Item} -ArgumentList @CopyParams

感谢您的帮助。

【问题讨论】:

  • 有助于查看ScriptBlock 中的内容 - 假设这就是拉动args[0] 并期望它成为哈希的原因?还假设这是错误消息的来源?
  • 感谢arco444的回复。 ScriptBlock 只是一个在[String]Source[String]Destination 上的参数为Test-path 的函数。在此之后,它只使用Copy-Item 来复制内容。将它们作为-ArgumentList $Source, $Destination 传递时它工作正常,但不是飞溅。
  • 如果你用我最后一个例子Copy-Item 试试,你会发现它不起作用。甚至没有美元符号。

标签: powershell argument-passing invoke-command


【解决方案1】:

这是传递命名参数的一种方法:

function Copy-FilesHC 
{
  param ($Source,$Destination,$Structure)
  "Source is $Source"
  "Desintation is $Destination"
  "Structure is $Structure"
  }


$CopyParams = @{
    Source      = 'E:\DEPARTMENTS\CBR\SHARE\Target'
    Destination = "'E:\DEPARTMENTS\CBR\SHARE\Target 2'" #Nested quotes required due to embedded space in value.
    Structure   = 'yyyy-MM-dd'
}

$SB = [scriptblock]::Create(".{${Function:Copy-FilesHC}} $(&{$args}@CopyParams)")

Invoke-Command -Credential $Cred -ComputerName 'SERVER' -ScriptBlock $SB

基本上,您可以从调用的脚本创建一个新的脚本块,并将参数从哈希表中分散到该参数中。一切都已经在扩展值的脚本块中,因此没有要传递的参数列表。

【讨论】:

  • 谢谢 mjolinor,当Source 路径中没有空格时,它确实有效。如果有,它就会失败(例如Source = 'E:\DEPARTMENTS\CBR\SHARE\Target\De file.txt')。
  • 如果路径包含嵌入的空格,那么您必须传递一个显式引用的字符串,就像您在命令行中将其作为参数传递一样。在您使用哈希表来 splat 参数的任何情况下都是如此,而不仅仅是针对此应用程序。我只是错过了其中一个值在答案中嵌入了空格。如果要在本地会话中运行命令,则使用相同的哈希表,如果您想将参数添加到命令中。我用一个例子更新了答案。
【解决方案2】:

我找到了一种解决方法,但您必须确保位于模块文件中的 Advanced function 在本地会话中预先加载。所以它可以在远程会话中使用。我为此写了一个小帮助函数。

Function Add-FunctionHC {
    [CmdletBinding(SupportsShouldProcess=$True)]
    Param(
        [String]$Name
    )
    Process {
        Try {
            $Module = (Get-Command $Name -EA Stop).ModuleName
        }
        Catch {
            Write-Error "Add-FunctionHC: Function '$Name' doesn't exist in any module"
            $Global:Error.RemoveAt('1')
            Break
        }
        if (-not (Get-Module -Name $Module)) {
            Import-Module -Name $Module
        }
    }
}

# Load funtion for remoting
Add-FunctionHC -Name 'Copy-FilesHC'

$CopyParams = @{
    Source      = 'E:\DEPARTMENTS\CBR\SHARE\Target\De file.txt'
    Destination = 'E:\DEPARTMENTS\CBR\SHARE\Target 2'
}

$RemoteFunctions = "function Copy-FilesHC {${function:Copy-FilesHC}}" #';' seperated to add more

Invoke-Command -ArgumentList $RemoteFunctions -ComputerName 'SERVER' -Credential $Cred -ScriptBlock {
    Param (
        $RemoteFunctions
    )
    . ([ScriptBlock]::Create($RemoteFunctions))
    $CopyParams = $using:CopyParams
    Copy-FilesHC @CopyParams
}

最大的好处是您不需要在脚本中复制完整的功能,它可以保留在模块中。因此,当您将模块中的某些内容更改为函数时,它也将在远程会话中可用,而无需更新您的脚本。

【讨论】:

  • 这部分对我来说很关键:$CopyParams = $using:CopyParams.谢谢!
【解决方案3】:

我知道这已经晚了,但我遇到了同样的问题并找到了适合我的解决方案。将其分配给脚本块中的变量,然后使用该变量进行 splat 并没有显示任何问题。

这是一个例子:

$param=@{"parameter","value"}
invoke-command -asjob -session $session -ScriptBlock {$a=$args[0];cmdlet @a } -ArgumentList $param

【讨论】:

  • @{"parameter","value"} 是无效的哈希表语法,你真的测试过你发布的脚本吗?
【解决方案4】:

单线,将远程脚本转换为接受来自哈希的命名参数。

给定一个您希望这样调用的脚本块:

$Options = @{
    Parameter1 = "foo"
    Parameter2 = "bar"
}

Invoke-Command -ComputerName REMOTESERVER -ArgumentList $Options -ScriptBlock {
    param(
        $Parameter1,
        $Parameter2
    )
    #Script goes here, this is just a sample
    "ComputerName: $ENV:COMPUTERNAME"
    "Parameter1: $Parameter1"
    "Parameter2: $Parameter2"
} 

你可以这样转换

Invoke-Command -Computername REMOTESERVER -ArgumentList $Options -ScriptBlock {param($Options)&{
    param(
        $Parameter1,
        $Parameter2
    )
    #Script goes here, this is just a sample
    "ComputerName: $ENV:COMPUTERNAME"
    "Parameter1: $Parameter1"
    "Parameter2: $Parameter2"
} @Options}

发生了什么事?基本上我们已经像这样包装了原始脚本块:

{param($Options)& <# Original script block (including {} braces)#> @options }

这使原始脚本块成为匿名函数,并创建具有参数$Options 的外部脚本块,它除了调用内部脚本块之外什么都不做,传递@options 以散列哈希。

【讨论】:

  • 很棒的提示!谢谢本! :)
  • 相当优雅的解决方案:)。将您的原始(未更改)脚本块包装在一个简单的外部脚本块中,该脚本块执行所有解包(飞溅)。参考这里:about_Script_Blocks(搜索call operator)。
【解决方案5】:

我最近遇到了一个类似的问题,并通过利用 $using 变量范围在调用内部构建哈希(或重建哈希)解决了它(更多关于 here

看起来像这样:

$Source      = 'E:\DEPARTMENTS\CBR\SHARE\Target'
$Destination = 'E:\DEPARTMENTS\CBR\SHARE\Target 2'
$Structure   = 'yyyy-MM-dd'

Invoke-Command -Credential $Cred -ComputerName 'SERVER' -ScriptBlock {
    $CopyParms= @{ 
        'Source'=$Using:Source
        'Destination'=$Using:Destination
        'Structure'=$Using:Structure
    }
    Function:Copy-FilesHC @CopyParms
}

【讨论】:

    【解决方案6】:

    这对我有用:

    $hash = @{
        PARAM1="meaning of life"
        PARAM2=42
        PARAM3=$true
    }
    $params = foreach($x in $hash.GetEnumerator()) {"$($x.Name)=""$($x.Value)"""}
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-12-02
      • 1970-01-01
      • 1970-01-01
      • 2014-10-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多