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
foreach
declaració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.
- La necesidad de
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ónForEach-Object
cmdlet (%
).
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 deConvert-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 elForEach-Object
cmdlet.
Conclusiones generales, aplicable tanto al tipo .NET regular como [pscustomobject]
aporte:
-
La enumeración de miembros (
$collection.Name
) yforeach ($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 baseSelect-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.