在PowerShell中选择阵列的所有对象上的一个属性的值

Syl*_*rdy 115 arrays powershell member-enumeration

假设我们有一个对象数组$ objects.假设这些对象具有"名称"属性.

这就是我想要做的

 $results = @()
 $objects | %{ $results += $_.Name }
Run Code Online (Sandbox Code Playgroud)

这有效,但可以更好的方式完成吗?

如果我这样做:

 $results = objects | select Name
Run Code Online (Sandbox Code Playgroud)

$results是具有Name属性的对象数组.我希望$ results包含一个Name数组.

有没有更好的办法?

Sco*_*aad 196

我想你可以使用ExpandProperty参数Select-Object.

例如,要获取当前目录的列表并显示Name属性,可以执行以下操作:

ls | select -Property Name
Run Code Online (Sandbox Code Playgroud)

这仍然是返回DirectoryInfo或FileInfo对象.您可以通过管道到Get-Member(别名gm)来检查通过管道传输的类型.

ls | select -Property Name | gm
Run Code Online (Sandbox Code Playgroud)

因此,要将对象扩展为您正在查看的属性类型,您可以执行以下操作:

ls | select -ExpandProperty Name
Run Code Online (Sandbox Code Playgroud)

在您的情况下,您可以执行以下操作使变量成为字符串数组,其中字符串是Name属性:

$objects = ls | select -ExpandProperty Name
Run Code Online (Sandbox Code Playgroud)

  • 我应该将此页添加为书签。我已经多次使用这个答案。 (8认同)

rag*_*dqq 64

作为一个更简单的解决方案,您可以使用:

$results = $objects.Name
Run Code Online (Sandbox Code Playgroud)

哪个应该填充$results元素中所有'Name'属性值的数组$objects.

  • 请注意,这在“Exchange Management Shell”中不起作用。使用 Exchange 时,我们需要使用 `$objects | 选择-Property Propname,OtherPropname` (2认同)
  • @Bassie:在集合级别访问属性以获取其成员值作为数组称为_member enumeration_,并且是[PSv3 +功能](https://blogs.msdn.microsoft.com/powershell/2012/06/13 / new-v3-language-features /); 大概您的Exchange命令行管理程序是PSv2。 (2认同)

mkl*_*nt0 22

何时使用哪种方法性能比较的指导下,补充已有的,有用的答案.

  • 管道之外,使用:

    __PRE__
    (PSv3 +),如rageandqq的回答所示,它在语法上更简单,更快.

    • 访问集合级别的属性以将其成员的值作为数组获取称为成员枚举,并且是PSv3 +功能 ;
    • 或者,在PSv2中,使用foreach 语句,其输出也可以直接分配给变量:
      $objects.Name
    • 权衡:
      • 无论是输入收集和输出数组 必须适合存储器作为一个整体.
      • 如果输入集合本身是命令(管道)的结果(例如,(Get-ChildItem).Name),则该命令必须首先运行完成,然后才能访问结果数组的元素.
  • 必须进一步处理结果或结果不适合整个内存管道中,使用:

    __PRE__

    • Scott Saad的答案-ExpandProperty解释了需要.
    • 您可以获得一对一处理的常规管道优势,通常可以立即生成输出并保持内存使用不变(除非您最终将结果收集到内存中).
    • 权衡:
      • 使用管道相对较慢.

对于小型输入集合(数组),您可能不会注意到差异,特别是在命令行上,有时能够轻松地键入命令更为重要.


这是一种易于使用的替代方案,但这是最慢的方法 ; 它使用称为操作语句的简化ForEach-Object语法(同样,PSv3 +):; 例如,以下PSv3 +解决方案很容易附加到现有命令:

$results = foreach ($obj in $objects) { $obj.Name }

为了完整起见:鲜为人知的PSv4 + .ForEach() 收集方法是另一种选择:

$objects | Select-Object -ExpandProperty Name
  • 这种方法类似于成员枚举,具有相同的权衡,除了应用管道逻辑; 它稍微慢一些,但仍比管道快得多.

  • 为了通过名称(字符串参数)提取单个属性值,此解决方案与成员枚举相同(尽管后者在语法上更简单).

  • 脚本块的变体,尽管慢得多,允许任意变换 ; 它是一种更快 - 一次性内存 - 替代基于管道的ForEach-Object cmdlet.


比较各种方法的表现

以下是基于输入对象集合的各种方法的样本计时,平均为100次运行; 绝对数字并不重要,并且根据许多因素而有所不同,但它应该让您了解相对表现:%

$objects | % Name      # short for: $objects | ForEach-Object -Process { $_.Name }
Run Code Online (Sandbox Code Playgroud)
  • 基于member-enumeration/property-name的集合方法解决方案比最快的基于管道的解决方案快10倍.

  • 10,000 声明的解决方案是大约2.5速度较慢,但仍有约4-5倍的速度最快的管道解决方案.

  • 使用带有集合方法解决方案()的脚本块Get-ChildItem会大大减慢速度,因此它几乎与最快的基于管道的解决方案([pscustomobject])相当.

  • Convert-FromCsv([pscustomobject]好奇地,表现最差,即使%是概念等同于ForEach-Object).


测试的源代码:

注意:[pscustomobject]从此Gist下载函数以运行这些测试.

# By property name (string):
$objects.ForEach('Name')

# By script block (more flexibility; like ForEach-Object)
$objects.ForEach({ $_.Name })
Run Code Online (Sandbox Code Playgroud)