如何在 Powershell 5.1 中初始化自定义对象列表?
How to initialise a custom object list in Powershell 5.1?
我想达到的目标:
创建文件列表,显示它们的名称和版本并按字母顺序排序。
我的尝试:
class File_Information
{
[ValidateNotNullOrEmpty()][string]$Name
[ValidateNotNullOrEmpty()][string]$FileVersion
}
$FileList = New-Object System.Collections.Generic.List[File_Information]
foreach ($file in Get-Item("*.dll")){
$C = [File_Information]@{
Name = $file.Name
FileVersion = $file.VersionInfo.FileVersion
}
$FileList = $FileList.Add($C)
}
foreach ($file in Get-Item("*.exe")){
$C = [File_Information]@{
Name = $file.Name
FileVersion = $file.VersionInfo.FileVersion
}
$FileList = $FileList.Add($C)
}
Write-Output $FileList | Sort-Object -Property "Name" | Format-Table
我的 Powershell 版本:
Prompt> Get-Host | Select-Object Version
Version
-------
5.1.19041.1320
我的问题and/or个问题(第一个是大题,第二个和第三个是可选的):
我不知道如何初始化自定义对象列表(我一直在该站点上查找,但 this question 只提供了使用现有值进行初始化的可能性,而我想初始化为一个空列表)。
我收到的错误是:
You cannot call a method on a null-valued expression.
At line:... char:...
+ $FileList = $FileList.Add($C)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
目前我要求同样的事情两次:我尝试了 Get-Item(*.dll,*.exe)
、Get-Item(*.dll;*.exe)
和 Get-Item(*.dll|*.exe)
,但其中 none 成功了。
甚至可以搜索不同的模式吗?
直到现在我都在努力让 Write-Output $FileList
工作。另外两个命令(Sort-Object -Property "Name"
、Format-Table
)是否正确?
为什么要为此使用 class 和 List 对象?
您可以只对使用 Get-ChildItem
收集的感兴趣的文件使用 Select-Object
并在那里输出所需的属性:
# capture the resulting objects in a variable $result
$result = Get-ChildItem -Path 'X:\path\to\the\files' -File -Include '*.dll','*.exe' -Recurse |
Select-Object Name, @{Name = 'FileVersion'; Expression = {$_.VersionInfo.FileVersion}}
# output on screen
$result | Sort-Object Name | Format-Table -AutoSize
要在 Get-ChildItem
上使用两种不同的名称模式,您还需要添加开关 -Recurse
,或将 \*
附加到路径。
如果您不想递归,则需要添加一个 Where-Object
子句,例如:
$result = Get-ChildItem -Path 'X:\path with spaces' | Where-Object {$_.Extension -match '\.(dll|exe)'} |
Select-Object Name, @{Name = 'FileVersion'; Expression = {$_.VersionInfo.FileVersion}}
您的代码实际上工作正常,只有两个小例外。
问题 #1:过度限制 属性 属性会导致错误
首先,在您的自定义 class 中,您应该非常确定要附加此属性 [ValidateNotNullOrEmpty()]
,因为当您尝试新建时它会抛出 non-terminating 错误如果具有此属性的任何一个属性为空,则创建 class 的实例。
例如,在我的 PC 上,我有一两个 file.VersionInfo.FileVersion
属性为空的 dll。发生这种情况时,PowerShell 将无法创建该对象,然后也无法尝试将该对象添加到列表中,因为没有对象。很混乱。
Fix:删除 ValidateNotNullOrEmpty 的属性,除非您 100% 肯定此信息存在。
接下来,真正的问题是在你的每个for-each循环中,你不小心打破了您的 FileList
您在代码中正确设置的对象,因为它已经存在。
问题 #2:小的语法问题导致您的列表被清空,导致第一个操作之后的每个操作都出错。
问题出现在下面这一行,我会解释。
$FileList = $FileList.Add($C)
List.Add() 方法的 return 类型是 void 或整数(当您提供通用对象时它只是一个 int,如果您提供为列表指定的类型,在您的案例是 File_Information
的自定义 class,它提供了一个 VOID。)
void 与 null 相同。我们可以通过以下代码确认.Add的输出与$null
相同。
PS> $FileList.Add($c) -eq $null
True
您只需键入不带参数的方法名称,就可以详细查看调用 .Add()
的各种方式的输出,如下所示。
PS> $FileList.Add
OverloadDefinitions
-------------------
void Add(File_Information item) #< the one you're using!
void ICollection[File_Information].Add(File_Information item)
int IList.Add(System.Object value)
因此,实际上您的 for each 循环是将一个项目添加到列表中,然后将变量 $FileList 重新保存到 .Add 方法的输出中,换句话说,它是一个空值,即 null。你正在扼杀自己的名单!
我们不需要重新保存 $FileList
,因为列表的目的是在代码中填充,我们唯一要做的就是调用 .Add()
方法。
修复:通过更改代码中的这些行,停止在循环中杀死列表对象。
#before
$FileList = $FileList.Add($C)
#after
$FileList.Add($C)
我想达到的目标:
创建文件列表,显示它们的名称和版本并按字母顺序排序。
我的尝试:
class File_Information
{
[ValidateNotNullOrEmpty()][string]$Name
[ValidateNotNullOrEmpty()][string]$FileVersion
}
$FileList = New-Object System.Collections.Generic.List[File_Information]
foreach ($file in Get-Item("*.dll")){
$C = [File_Information]@{
Name = $file.Name
FileVersion = $file.VersionInfo.FileVersion
}
$FileList = $FileList.Add($C)
}
foreach ($file in Get-Item("*.exe")){
$C = [File_Information]@{
Name = $file.Name
FileVersion = $file.VersionInfo.FileVersion
}
$FileList = $FileList.Add($C)
}
Write-Output $FileList | Sort-Object -Property "Name" | Format-Table
我的 Powershell 版本:
Prompt> Get-Host | Select-Object Version
Version
-------
5.1.19041.1320
我的问题and/or个问题(第一个是大题,第二个和第三个是可选的):
我不知道如何初始化自定义对象列表(我一直在该站点上查找,但 this question 只提供了使用现有值进行初始化的可能性,而我想初始化为一个空列表)。
我收到的错误是:You cannot call a method on a null-valued expression. At line:... char:... + $FileList = $FileList.Add($C) + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : InvalidOperation: (:) [], RuntimeException + FullyQualifiedErrorId : InvokeMethodOnNull
目前我要求同样的事情两次:我尝试了
Get-Item(*.dll,*.exe)
、Get-Item(*.dll;*.exe)
和Get-Item(*.dll|*.exe)
,但其中 none 成功了。
甚至可以搜索不同的模式吗?直到现在我都在努力让
Write-Output $FileList
工作。另外两个命令(Sort-Object -Property "Name"
、Format-Table
)是否正确?
为什么要为此使用 class 和 List 对象?
您可以只对使用 Get-ChildItem
收集的感兴趣的文件使用 Select-Object
并在那里输出所需的属性:
# capture the resulting objects in a variable $result
$result = Get-ChildItem -Path 'X:\path\to\the\files' -File -Include '*.dll','*.exe' -Recurse |
Select-Object Name, @{Name = 'FileVersion'; Expression = {$_.VersionInfo.FileVersion}}
# output on screen
$result | Sort-Object Name | Format-Table -AutoSize
要在 Get-ChildItem
上使用两种不同的名称模式,您还需要添加开关 -Recurse
,或将 \*
附加到路径。
如果您不想递归,则需要添加一个 Where-Object
子句,例如:
$result = Get-ChildItem -Path 'X:\path with spaces' | Where-Object {$_.Extension -match '\.(dll|exe)'} |
Select-Object Name, @{Name = 'FileVersion'; Expression = {$_.VersionInfo.FileVersion}}
您的代码实际上工作正常,只有两个小例外。
问题 #1:过度限制 属性 属性会导致错误
首先,在您的自定义 class 中,您应该非常确定要附加此属性 [ValidateNotNullOrEmpty()]
,因为当您尝试新建时它会抛出 non-terminating 错误如果具有此属性的任何一个属性为空,则创建 class 的实例。
例如,在我的 PC 上,我有一两个 file.VersionInfo.FileVersion
属性为空的 dll。发生这种情况时,PowerShell 将无法创建该对象,然后也无法尝试将该对象添加到列表中,因为没有对象。很混乱。
Fix:删除 ValidateNotNullOrEmpty 的属性,除非您 100% 肯定此信息存在。
接下来,真正的问题是在你的每个for-each循环中,你不小心打破了您的 FileList
您在代码中正确设置的对象,因为它已经存在。
问题 #2:小的语法问题导致您的列表被清空,导致第一个操作之后的每个操作都出错。
问题出现在下面这一行,我会解释。
$FileList = $FileList.Add($C)
List.Add() 方法的 return 类型是 void 或整数(当您提供通用对象时它只是一个 int,如果您提供为列表指定的类型,在您的案例是 File_Information
的自定义 class,它提供了一个 VOID。)
void 与 null 相同。我们可以通过以下代码确认.Add的输出与$null
相同。
PS> $FileList.Add($c) -eq $null
True
您只需键入不带参数的方法名称,就可以详细查看调用 .Add()
的各种方式的输出,如下所示。
PS> $FileList.Add
OverloadDefinitions
-------------------
void Add(File_Information item) #< the one you're using!
void ICollection[File_Information].Add(File_Information item)
int IList.Add(System.Object value)
因此,实际上您的 for each 循环是将一个项目添加到列表中,然后将变量 $FileList 重新保存到 .Add 方法的输出中,换句话说,它是一个空值,即 null。你正在扼杀自己的名单!
我们不需要重新保存 $FileList
,因为列表的目的是在代码中填充,我们唯一要做的就是调用 .Add()
方法。
修复:通过更改代码中的这些行,停止在循环中杀死列表对象。
#before
$FileList = $FileList.Add($C)
#after
$FileList.Add($C)