PowerShell:将对象添加到对象数组

57

我有这样一个脚本,在每个foreach循环中,我想把一个对象添加到名为$Target的数组中。

foreach ($Machine in $Machines)
{
  $TargetProperties = @{Name=$Machine}  
  $TargetObject = New-Object PSObject –Property $TargetProperties
  $Target= @()
  $Target =  $TargetObject
}

我知道它不起作用是因为$Target = $TargetObject使它等于同一个对象。

我该如何将内容附加到数组而不是替换它?

3个回答

117

要将数据添加到数组中,只需使用+=运算符。

$Target += $TargetObject

另外,在您的循环之前需要声明$Target = @(),否则它会在每次循环时清空数组。


11
+= 会杀掉小狗。 - js2010

14
我知道这是一个老问题,但对于未来的读者来说,接受的答案并不是在每种情况下都是理想的,特别是在循环遍历大量数据集时。
问题是如何"向对象数组中添加对象"。这篇帖子的标题与给出的示例问题并不适合同一个答案。相反,我旨在解决示例中展示的问题。
虽然使用数组的追加运算符+=很容易,但在幕后,这是一个相当浪费资源的过程。浪费的程度取决于您正在遍历的对象的大小。
接受的答案从一个初始化的空数组$Target = @()开始。然后建议通过循环$Machines将项目追加到空数组中,使用$Target += $TargetObject。在幕后,PowerShell测量$Target的大小和你要追加的项目$TargetObject的大小,将这些大小相加并创建一个全新的数组。然后将$Target的内容复制/添加到新数组中,然后再添加要追加的对象。这个过程在每个循环项中都会发生。如果$Machines中有1000个项目,那么在操作结束时,你将创建1000个数组。
所有这些都是因为普通的数组是固定大小的。通过向具有固定大小的对象追加项目,我们最终得到了这种浪费资源的过程。我们可以通过根本不创建新数组来避免这种情况。相反,只需将foreach循环已经创建的数组分配给一个变量。请参考以下示例。
$Target = foreach ($Machine in $Machines)
{
  # Stuff I want to be in $Target goes here
}

其中一个其他答案有正确的想法,但给出的示例是不正确的。为了让任何东西最终出现在$Target变量中,您必须确保数据被发送到成功输出流。不要将其与使用类似Write-Host的东西写入信息流的数据混淆。
这是一个有效的示例:
# Mock array for illustration purposes
$Machines = @( "Machine1", "Machine2", "Machine3" ) 
    
$Target = foreach ( $Machine in $Machines )
{
    # This is being sent directly to the success output stream and therefore, into the $Target variable     
    @{ Name = $Machine } 
}

你的输出应该像这样:

名称 数值
名称 机器1
名称 机器2
名称 机器3

如果你熟悉Cmdlet Foreach-Object,下面是等效的。你可以开始看到与我第一个示例的相似之处。

$Target = $Machines | ForEach-Object { @{ Name = $_ } }

这些文章详细介绍了我刚才提到的所有内容以及性能影响。

1
这是一个非常好的、清晰的解释,绝对是在生产系统上经常使用的脚本中所要采取的方式。 - Steve
对于你的管道示例,我认为你想要 %{ @{ Name = $_ } }... 在第二个示例中使用 $_ 而不是 $Machine,因为 $Machine 变量在其中从未定义并且将为空。 - immobile2
@immobile2 这是我的疏忽。你是正确的,谢谢你指出来。已修复。 - HiTech
如果目标是根据对象类型创建2个数组,或者在我的情况下是从Get-Child命令获取的文件,那么在这种情况下如何添加项目?我根据最后写入时间的月份向数组中添加项目。它将始终是本月或上个月,有时仅为本月。$auditArray = @(Get-ChildItem -Path $backupPath*$auditExtension | Sort-Object -Property LastWriteTime) 然后迭代该数组以将项目添加到2个数组中。我想我可以执行2个Get-Child命令,但我认为这样更清晰。 - mappingman
@mappingman,我不确定我理解你想要的结果。如果我想要创建多个变量,这些变量包含基于属性LastWriteTime中的月份找到的对象,我会这样做。使用你的例子,一旦我从Get-ChildItem命令中获得了所有的对象($auditArray),我会创建新的变量,只过滤出我需要的对象。例如$objectsInOct = $auditArray | Where-Object {$_.LastWriteTime.Month -eq 10} $objectsInNov = $auditArray | Where-Object {$_.LastWriteTime.Month -eq 11} - undefined

2
通常人们在使用不高效的"+="时,会每次创建一个新的数组副本来实现此操作。
$Target = foreach ($Machine in $Machines)
{
  $TargetProperties = @{Name=$Machine}  
  $TargetObject = New-Object PSObject –Property $TargetProperties
}

1
这段代码需要一些调整,但实际上相当不错。目前,如果运行foreach循环,它并不会输出任何内容。这是因为$Target = 实际上并没有捕获到任何东西。删除$TargetObject =这一行将导致foreach循环输出一系列的对象。依次,这些对象被原始的$Target = 行所捕获,现在你有了一个漂亮的对象数组。非常好。 - lordsampigans

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接