Powershell 脚本块截断数组上的Invoke方法

Powershell 脚本块截断数组上的Invoke方法,powershell,Powershell,我已定义以下脚本块: [ScriptBlock]$strSb = { param( [Parameter(Mandatory=$false,Position=0)] [String[]]$Modules = @('String3','String4') ) Write-Host "Passed in params:" foreach($m in $Modules){ Write-Host $m } $

我已定义以下脚本块:

[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中工作。

不确定确切原因,但它似乎不喜欢那个命名参数。似乎喜欢$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

不确定确切原因,但它似乎不喜欢那个命名参数。似乎喜欢$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

如果我理解你在做什么,你需要2个数组,连接它们,并确保唯一性

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

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

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

[Parameter(ValueFromRemainingArguments=$true, Position=0, Mandatory=$true)]
       [String[]]$Modules
但是,对于Join,您可以更轻松地执行以下操作:

($modules + $defaultModules) | Select -Unique

如果我理解你在做什么,你需要2个数组,连接它们,并确保唯一性

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

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

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

[Parameter(ValueFromRemainingArguments=$true, Position=0, Mandatory=$true)]
       [String[]]$Modules
但是,对于Join,您可以更轻松地执行以下操作:

($modules + $defaultModules) | Select -Unique

问题是,Invoke方法接受一个参数数组(类似于具有-ArgumentList参数的命令),因此数组中的每个元素都被解析为一个单独的参数。第一个参数
'String11'
,被分配给第一个位置参数$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
}

问题是,Invoke方法接受一个参数数组(类似于具有-ArgumentList参数的命令),因此数组中的每个元素都被解析为一个单独的参数。第一个参数
'String11'
,被分配给第一个位置参数$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
}

谢谢这就是我要找的。我的持续调查使我相信它将数组视为参数列表。这回答了如何让它将数组视为参数。这将需要我几天来确认这是工作。当我做的时候,我会把这个标记为答案。谢谢!这就是我要找的。我的持续调查使我相信它将数组视为参数列表。这回答了如何让它将数组视为参数。这将需要我几天来确认这是工作。当我这样做时,我会将此标记为答案。感谢您的建议,但是由于某些原因,
来自RemaingArguments的值不起作用。它仍然只使用第一个值。我最终还是使用了加入建议