在ScriptBlock截断数组上调用方法

时间:2013-11-26 23:13:30

标签: powershell

我定义了以下ScriptBlock:

[ScriptBlock]$strSb = {
    param(
        [Parameter(Mandatory=$false,Position=0)]
        [String[]]$Modules = @('String3','String4')
    )
    Write-Host "Passed in params:"
    foreach($m in $Modules){
        Write-Host $m
    }
    $defaultModules = @('String3','String4')
    # Add Default Modules back if not present #
    foreach($module in $defaultModules){
        if($Modules -notcontains $module){
            $Modules += $module
        }
    }
    Write-Host "Final set:"
    # Load Dependencies #
    foreach($m in $Modules){
        Write-Host $m
    }
}

作为ScriptBlock中的参数状态,我希望能够传入一个字符串数组。当我致电$strSb.Invoke(@('String11','String12'))时,我会收到以下内容:

Passed in params:
String11
Final set:
String11
String3
String4

我的期望是:

Passed in params:
String11
String12
Final set:
String11
String12
String3
String4

为什么invoke方法会将我的数组截断为输入的第一个项目?我将如何修复它以便传入一串字符串?

FWIW:我在v2和v3工作。

3 个答案:

答案 0 :(得分:3)

问题是 Invoke 方法接受一组参数(类似于具有 -ArgumentList 参数的命令),因此解析了数组中的每个元素作为一个单独的论点。第一个参数'String11'被分配给第一个postitional参数 $ Modules ,并且任何后续参数都被丢弃,因为没有更多的位置参数。将 $ Modules 声明为字符串数组并不重要;由于参数列表的每个元素都是一个单独的参数,因此您将 $ Modules 设置为一个元素的数组。

如果使用,运算符来表示您传入的是单个数组参数,则它可以按预期工作:

$strSb.Invoke((,@('String11','String12')))
顺便说一下,你真的不需要@,因为默认情况下,以逗号分隔的字符串列表会被解释为数组。不只是在这个特定的背景下,而是一般而言。所以只需使用它:

$strSb.Invoke((,('String11','String12')))

要证明上面的解释,请尝试使用此脚本块,除了声明第二个参数(创造性地命名为 $ SecondParameter ),然后在显示值的循环后显示第一个参数:

[ScriptBlock]$strSb = {
    param(
        [Parameter(Mandatory=$false,Position=0)]
        [String[]]$Modules = @('String3','String4'),
        [String]$SecondParameter
    )
    Write-Host "Passed in params:"
    foreach($m in $Modules){
        Write-Host $m
    }
    Write-Host "`nSecondParameter: $SecondParameter`n"
    $defaultModules = @('String3','String4')
    # Add Default Modules back if not present #
    foreach($module in $defaultModules){
        if($Modules -notcontains $module){
            $Modules += $module
        }
    }
    Write-Host "Final set:"
    # Load Dependencies #
    foreach($m in $Modules){
        Write-Host $m
    }
}

如果您按原样传递参数$strSb.Invoke(@('String11','String12')),则会得到以下结果:

11-26-13 19:02:12.55 D:\Scratch\soscratch» $strSb.Invoke(@('String11','String12'))
Passed in params:
String11

SecondParameter: String12

Final set:
String11
String3
String4
11-26-13 19:02:29.34 D:\Scratch\soscratch»

与问题没有直接关系的最后一个提示是,您可以使用管道压缩 foreach 循环,这不仅更简洁,而且通常更有效。这是代码的压缩版本:

[ScriptBlock]$strSb = {
    param(
        [Parameter(Mandatory=$false,Position=0)] 
        [String[]]$Modules = ('String3','String4')
    )
    Write-Host "Passed in params:"
    $Modules | Write-Host
    $defaultModules = 'String3','String4'
    # Add Default Modules back if not present #
    $defaultModules | ?{$Modules -notcontains $_} | %{$Modules += $_}
    Write-Host "Final set:"
    # Load Dependencies #
    $Modules | Write-Host
}

答案 1 :(得分:1)

如果我理解你在做什么,你想要取两个数组,连接它们,并确保它们的独特性......

首先,由于参数上有[Parameter...],因此您可以在方法上获得[CmdletBinding()]。这意味着您将自动将$ Modules拆分为多个调用。

其次,ScriptBlock.Invoke()接受一个params样式数组,并将它们作为单独的参数放入方法中。

我要尝试的第一件事是添加属性来收集所有值:

[Parameter(ValueFromRemainingArguments=$true, Position=0, Mandatory=$true)]
       [String[]]$Modules

但是,对于Join,您可以更轻松地执行以下操作:

($modules + $defaultModules) | Select -Unique

答案 2 :(得分:0)

不确定原因,但它似乎不喜欢那个命名参数。似乎喜欢$ args,tho:

[ScriptBlock]$strSb = {
  $Modules = $args
  Write-Host "Passed in params:"
  foreach($m in $modules){
      Write-Host $m

   }
   $defaultModules = @('String3','String4')
   # Add Default Modules back if not present #
   foreach($module in $defaultModules){
       if($Modules -notcontains $module){
           $Modules += $module
       }
   }
   Write-Host "Final set:"
   # Load Dependencies #
   foreach($m in $Modules){
       Write-Host $m
   }
}


$strSb.Invoke('String11','String12')

Passed in params:
String11
String12
Final set:
String11
String12
String3
String4