Saltar al contenido

Seleccione los valores de una propiedad en todos los objetos de una array en PowerShell

Ya no necesitas investigar más por todo internet porque estás al lugar adecuado, poseemos la respuesta que buscas y sin complicarte.

Solución:

Creo que podrías usar el ExpandProperty parámetro de Select-Object.

Por ejemplo, para obtener la lista del directorio actual y solo mostrar la propiedad Name, se haría lo siguiente:

ls | select -Property Name

Esto sigue devolviendo objetos DirectoryInfo o FileInfo. Siempre puede inspeccionar el tipo que viene a través de la canalización mediante la canalización a Get-Member (alias gm).

ls | select -Property Name | gm

Entonces, para expandir si el objeto es el del tipo de propiedad que está viendo, puede hacer lo siguiente:

ls | select -ExpandProperty Name

En su caso, puede hacer lo siguiente para que una variable sea un array de cadenas, donde las cadenas son la propiedad Name:

$objects = ls | select -ExpandProperty Name

Como solución aún más sencilla, puede usar:

$results = $objects.Name

Que debería llenar $results con un array de todos los valores de propiedad ‘Nombre’ de los elementos en $objects.

Para complementar las respuestas útiles preexistentes con la guía de cuándo usar qué enfoque y un comparación de rendimiento.

  • Fuera de de una tubería[1], utilice (PSv3 +):

    $objects.Name

    como se demuestra en la respuesta de rageandqq, que es sintácticamente más simple y mucho mas rápido.

    • Accediendo a una propiedad en el colección nivel para obtener su miembros valores como array se llama enumeración de miembros y es una función de PSv3 +.

    • Alternativamente, en PSv2, utilizar el foreachdeclaración, cuya salida también puede asignar directamente a una variable:

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

    • Si primero es factible recopilar toda la salida de un comando (canalización) en la memoria, tú también puedes combinar canalizaciones con enumeración de miembros; p.ej:

       (Get-ChildItem -File | Where-Object Length -lt 1gb).Name
      
    • Compensaciones:

      • Ambos recogida de entrada y salida arraydebe caber en la memoria como un todo.
      • Si la colección de entrada es en sí misma el resultado de un comando (canalización) (p. Ej., (Get-ChildItem).Name), ese el comando debe ejecutarse primero hasta completarse antes de la resultante arraySe puede acceder a los elementos de.
  • en un tubería, en caso de que deba pasar los resultados a otro comando, especialmente si la entrada original no cabe en la memoria como un todo, usar:

    $objects | Select-Object -ExpandProperty Name

    • La necesidad de -ExpandProperty se explica en la respuesta de Scott Saad (lo necesita para obtener solo la propiedad valor).
    • Obtiene los beneficios habituales de la canalización de la canalización transmisión comportamiento, es decir, procesamiento de objetos uno por uno, que normalmente produce una salida de inmediato y mantiene constante el uso de la memoria (a menos que finalmente recopile los resultados en la memoria de todos modos).
    • Compensación:
      • Uso del la tubería es comparativamente lento.

Para pequeña colecciones de entrada (matrices), probablemente no notará la diferenciay, especialmente en la línea de comandos, a veces es más importante poder escribir el comando fácilmente.


Aquí hay un alternativa fácil de escribir, que, sin embargo, es el el más lento Acercarse; usa simplificado ForEach-Object sintaxis llamada declaración de operación (nuevamente, PSv3 +):; Por ejemplo, la siguiente solución PSv3 + es fácil de agregar a un comando existente:

$objects | % Name      # short for: $objects | ForEach-Object -Process  $_.Name 

los PSv4 + .ForEach()array método, que se analiza de forma más exhaustiva en este artículo, otra alternativa de buen rendimiento, pero ten en cuenta que requiere recopilar todas las entradas en la memoria primero, al igual que la enumeración de miembros:

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

# By script block (more flexibility; like ForEach-Object)
$objects.ForEach( $_.Name )
  • Este enfoque es similar a la enumeración de miembros, con las mismas compensaciones, excepto que la lógica de la canalización es no aplicado; está marginalmente más lento que enumeración de miembros, aunque todavía notablemente más rápido que el tubería.

  • Para extraer un solo valor de propiedad mediante nombre (string argumento), esta solución está a la par con la enumeración de miembros (aunque esta última es sintácticamente más simple).

  • los bloque de secuencia de comandos variante ... ) permite arbitrario transformaciones; está una alternativa más rápida, todo en memoria a la vez, a la basada en canalización ForEach-Objectcmdlet (%).

Nota la .ForEach() array método, como su .Where() hermano (el equivalente en memoria de Where-Object), siempre devuelve un colección (una instancia de [System.Collections.ObjectModel.Collection[psobject]]), incluso si solo uno se produce el objeto de salida.
Por el contrario, la enumeración de miembros, Select-Object, ForEach-Object y Where-Object devolver un único objeto de salida tal cual, sin envolverlo en una colección (array).


Comparación del rendimiento de los distintos enfoques

Aquí están tiempos de muestra para los diversos enfoques, basados ​​en una colección de insumos de 10,000 objetos, promediado en 10 carreras; los números absolutos no son importantes y varían en función de muchos factores, pero deberían darte una idea de relativo rendimiento (los tiempos provienen de una VM de Windows 10 de un solo núcleo:

Importante

  • El rendimiento relativo varía en función de si los objetos de entrada son instancias de tipos de .NET regulares (p. ej., como resultado de Get-ChildItem) o [pscustomobject] instancias (p. ej., como resultado de Convert-FromCsv).

    La razón es que [pscustomobject] PowerShell administra dinámicamente las propiedades y puede acceder a ellas más rápidamente que las propiedades normales de un tipo .NET normal (definido estáticamente). Ambos escenarios se tratan a continuación.

  • Las pruebas utilizan colecciones que ya están en memoria en su totalidad como entrada, para centrarse en el rendimiento puro de extracción de propiedades. Con una llamada de función / cmdlet de transmisión como entrada, las diferencias de rendimiento generalmente serán mucho menos pronunciadas, ya que el tiempo dedicado a esa llamada puede representar la mayor parte del tiempo invertido.

  • Por brevedad, alias % se utiliza para el ForEach-Object cmdlet.

Conclusiones generales, aplicable tanto al tipo .NET regular como [pscustomobject] aporte:

  • La enumeración de miembros ($collection.Name) y foreach ($obj in $collection) las soluciones son, con mucho, las más rápidas, por un factor de 10 o más rápido que la solución basada en canalización más rápida.

  • Asombrosamente, % Name funciona mucho peor que % $_.Name – ver este problema de GitHub.

  • PowerShell Core supera consistentemente a Windows Powershell aquí.

Tiempos con tipos de .NET regulares:

  • PowerShell Core v7.0.0-preview.3
Factor Command                                       Secs (10-run avg.)
------ -------                                       ------------------
1.00   $objects.Name                                 0.005
1.06   foreach($o in $objects)  $o.Name            0.005
6.25   $objects.ForEach('Name')                      0.028
10.22  $objects.ForEach( $_.Name )                 0.046
17.52  $objects | %  $_.Name                       0.079
30.97  $objects | Select-Object -ExpandProperty Name 0.140
32.76  $objects | % Name                             0.148
  • Windows PowerShell v5.1.18362.145
Factor Command                                       Secs (10-run avg.)
------ -------                                       ------------------
1.00   $objects.Name                                 0.012
1.32   foreach($o in $objects)  $o.Name            0.015
9.07   $objects.ForEach( $_.Name )                 0.105
10.30  $objects.ForEach('Name')                      0.119
12.70  $objects | %  $_.Name                       0.147
27.04  $objects | % Name                             0.312
29.70  $objects | Select-Object -ExpandProperty Name 0.343

Conclusiones:

  • En PowerShell Centro, .ForEach('Name') supera claramente .ForEach( $_.Name ). En Windows PowerShell, curiosamente, este último es más rápido, aunque solo marginalmente.

Tiempos con [pscustomobject] instancias:

  • PowerShell Core v7.0.0-preview.3
Factor Command                                       Secs (10-run avg.)
------ -------                                       ------------------
1.00   $objects.Name                                 0.006
1.11   foreach($o in $objects)  $o.Name            0.007
1.52   $objects.ForEach('Name')                      0.009
6.11   $objects.ForEach( $_.Name )                 0.038
9.47   $objects | Select-Object -ExpandProperty Name 0.058
10.29  $objects | %  $_.Name                       0.063
29.77  $objects | % Name                             0.184
  • Windows PowerShell v5.1.18362.145
Factor Command                                       Secs (10-run avg.)
------ -------                                       ------------------
1.00   $objects.Name                                 0.008
1.14   foreach($o in $objects)  $o.Name            0.009
1.76   $objects.ForEach('Name')                      0.015
10.36  $objects | Select-Object -ExpandProperty Name 0.085
11.18  $objects.ForEach( $_.Name )                 0.092
16.79  $objects | %  $_.Name                       0.138
61.14  $objects | % Name                             0.503

Conclusiones:

  • Note como con [pscustomobject] aporte .ForEach('Name') supera con creces la variante basada en bloques de script, .ForEach( $_.Name ).

  • Similar, [pscustomobject] la entrada hace que la canalización se base Select-Object -ExpandProperty Name más rápido, en Windows PowerShell prácticamente a la par con .ForEach( $_.Name ), pero en PowerShell Core sigue siendo un 50% más lento.

  • En resumen: con la extraña excepción de % Name, con [pscustomobject] los stringLos métodos basados ​​en referenciar las propiedades superan a los basados ​​en bloques de script.


Código fuente de las pruebas:

Nota:

  • Función de descarga Time-Command de este Gist para ejecutar estas pruebas.

    • Suponiendo que haya mirado el código vinculado para asegurarse de que sea seguro (lo que personalmente puedo asegurarle, pero siempre debe verificar), puede instalarlo directamente de la siguiente manera:

      irm https://gist.github.com/mklement0/9e1f13978620b09ab2d15da5535d1b27/raw/Time-Command.ps1 | iex
      
  • Colocar $useCustomObjectInput para $true para medir con [pscustomobject] instancias en su lugar.

$count = 1e4 # max. input object count == 10,000
$runs  = 10  # number of runs to average 

# Note: Using [pscustomobject] instances rather than instances of 
#       regular .NET types changes the performance characteristics.
# Set this to $true to test with [pscustomobject] instances below.
$useCustomObjectInput = $false

# Create sample input objects.
if ($useCustomObjectInput) 
  # Use [pscustomobject] instances.
  $objects = 1..$count  else 
  # Use instances of a regular .NET type.
  # Note: The actual count of files and folders in your file-system
  #       may be less than $count
  $objects = Get-ChildItem / -Recurse -ErrorAction Ignore 

Write-Host "Comparing property-value extraction methods with $($objects.Count) input objects, averaged over $runs runs..."

# An array of script blocks with the various approaches.
$approaches =  $objects ,
               % Name ,
               %  $_.Name  ,
               $objects.ForEach('Name') ,
               $objects.ForEach( $_.Name ) ,
               $objects.Name ,
               foreach($o in $objects)  $o.Name  

# Time the approaches and sort them by execution time (fastest first):
Time-Command $approaches -Count $runs | Select Factor, Command, Secs*

[1] Técnicamente, incluso un comando sin |, el operador de la tubería, usa una tubería detrás de escena, pero para el propósito de esta discusión usando la tubería se refiere solo a los comandos que hacer usar | y por lo tanto involucrar varios comandos conectado por una tubería.

Sección de Reseñas y Valoraciones

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *