我有一个大型数组(从Excel电子表格导入),它有一个我要删除的"总"行.它始终是数组中的最后一项.我试图找到一种方法来删除最后一个数组项,但不能.以下数据和代码示例:
$data = Import-XLSX -Path "C:\Pathtofile\spreadsheet.xlsx" -Sheet "SheetName" -RowStart 3
$data | ? {$_.Server -eq "Total"}
Server : Total
VLAN :
IP Address :
CPU : 84
RAM : 313
C:\ : 2840
D:\ : 17950
OS Version :
OS Edition :
SQL Version :
SQL Edition :
Datastore :
Reboot :
Notes :
Run Code Online (Sandbox Code Playgroud)
我希望能够从数组中删除此行.我尝试过以下方法:
$data.Remove("Total")
Exception calling "Remove" with "1" argument(s): "Collection was of a fixed size."
At line:1 char:1
+ $ImportCSV.Remove("Total")
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : NotSupportedException
Run Code Online (Sandbox Code Playgroud)
如果我检查数组类型,我得到以下内容:
$data.GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Object[] System.Array
Run Code Online (Sandbox Code Playgroud)
如果我像这样重新键入对象,我得到以下内容:
$data = {$data}.Invoke()
$data.GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Collection`1 System.Object
$data.Remove("Total")
False
Run Code Online (Sandbox Code Playgroud)
这不会删除该项目.我在这里错过了什么?
Tes*_*ler 19
数组在PowerShell中是不可变的,您无法添加或删除它们中的元素.你能做的是:
a)创建一个新数组并过滤掉你不想要的数组(例如:)-ne:
$data = $data | ? {$_.Server -ne "Total"}
Run Code Online (Sandbox Code Playgroud)
要么
b)按索引选择最后一个元素的所有内容(可能是大数据库上的资源,我不知道):
$data = $data[0..($data.Count-2)]
Run Code Online (Sandbox Code Playgroud)
要么
c)将其转换[System.Collections.ArrayList]为可变的,然后从中删除一个元素:
$data = [System.Collections.ArrayList]$data
$data.RemoveAt($data.Count-1)
Run Code Online (Sandbox Code Playgroud)
通过分析和背景信息补充TessellatingHeckler的有用答案:
如所陈述,PowerShell的阵列(.NET类型[System.Object[]])是固定大小的 -你不能添加或删除的元素(可以,但是,改变值的现有元素).
当您使用+或"添加"元素到PowerShell数组时+=,会在后台创建一个新数组,这是原始数据的副本(浅层克隆).
数组具有.Add(),.Remove()和.RemoveAt()方法的唯一原因是它们实现了通用System.Collections.IList接口,该接口由许多不同类型的集合实现,其中一些集合是可调整大小的.
尝试在固定大小的集合(如数组)上调用这些方法会导致出现"Collection was of a fixed size."错误消息.
无论输入集合的类型如何,通过输出多于1个对象的管道发送任何内容都将作为(新)数组出现.(单个结果对象作为自身输出,不包含在集合中.)
$data | ? {$_.Server -ne "Total"}创建一个始终是数组的新集合(如果有超过1个输出对象).[System.Object[]]为什么尝试失败:
$data.Remove("Total")失败的根本,因为数组不支持拆卸,如提及.
但即使$data 是一个可调整大小的集合(例如[System.Collections.ArrayList]),传递"Total"也没有用,因为你试图调用的IList.Remove方法要求你传递元素来移除它自己,在你的情况下它将是其属性包含的对象,最容易作为.访问. .Server"Total"$data[-1]
另请注意,传递不是列表元素的值是安静的无操作(如果集合可调整大小).
鉴于您想要删除最后一个元素,基于索引的IList.RemoveAt方法将是更简单的选择.
{$data}.Invoke()确实会对各种类型进行重新输入,但最好避免以一种神秘的方式:
乍一看,从概念上讲,该命令应该是一个无操作:你只是将一个变量引用括在一个脚本块({ ... })中,然后调用它,它应该只输出变量的值.
它不是无操作的原因是你绕过PowerShell对脚本块的正常处理是通过.NET .Invoke()方法调用它而不是通常调用脚本块的方式:使用call operator &.
如果.Invoke()在脚本块上使用,则始终会获得一个[System.Collections.ObjectModel.Collection[psobject]]实例(即使它只包含一个对象).
相比之下,在幕后&调用.Invoke()后使用会做额外的工作:如果集合只包含1个元素,则直接返回该元素(集合被解包).否则,该集合将转换为PowerShell数组([System.Object[]];我不清楚此转换的具体情况).
既然你用.Invoke()直接呼叫到.Remove()上运行[System.Collections.ObjectModel.Collection[psobject]],这是可调整大小,并有自己的.Remove()方法,其排除逻辑是一样的IList.Remove(),它的阴影.
但是,它的不同之处IList.Remove()在于返回[bool]反射成功的移除尝试,这就是为什么你false在第二次尝试时获得了.
然而,与第一次尝试不同的是,这次失败不是基本的,并且传入最后一个元素所包含的对象(行); 同样,.RemoveAt()最高指数也会有效:
# Convert to [System.Collections.ObjectModel.Collection[psobject]]
$dataResizable = { $data }.Invoke()
# Remove last item by index.
$dataResizable.RemoveAt($dataResizable.Count-1)
# Alternative: Remove last item by object reference / value.
# Returns [bool], which we suppress.
$null = $dataResizable.Remove($dataResizable[-1])
Run Code Online (Sandbox Code Playgroud)
说了这么多,使用.Invoke()的脚本块只是为了获得一个可调整大小的集合是不明智的.
相反,选择TessellatingHeckler的答案中演示的方法之一.
此外,PSv5 +提供了一种从通过管道传递的集合中删除最后一个元素的简单方法Select-Object -SkipLast 1:
$data = Import-XLSX -Path "C:\Pathtofile\spreadsheet.xlsx" -Sheet "SheetName" -RowStart 3 |
Select-Object -SkipLast 1
Run Code Online (Sandbox Code Playgroud)