¿Cuál es la mejor manera (más limpia) de ignorar la salida en PowerShell? [cerrado]

134

Digamos que tiene un método o un cmdlet que devuelve algo, pero no desea usarlo y no desea generarlo. Encontré estas dos formas:

Add-Item > $null

[void]Add-Item

Add-Item | Out-Null

¿Que usas? ¿Cuál es el enfoque mejor / más limpio? ¿Por qué?

Hinek
fuente
1
como la versión [vacía] ... aún no ha sido para mí, pero intentaré recordarlo.
Macizo

Respuestas:

181

Acabo de hacer algunas pruebas de las cuatro opciones que conozco.

Measure-Command {$(1..1000) | Out-Null}

TotalMilliseconds : 76.211

Measure-Command {[Void]$(1..1000)}

TotalMilliseconds : 0.217

Measure-Command {$(1..1000) > $null}

TotalMilliseconds : 0.2478

Measure-Command {$null = $(1..1000)}

TotalMilliseconds : 0.2122

## Control, times vary from 0.21 to 0.24
Measure-Command {$(1..1000)}

TotalMilliseconds : 0.2141

Por lo tanto, sugeriría que use cualquier cosa menos Out-Nullpor sobrecarga. La siguiente cosa importante, para mí, sería la legibilidad. Me gusta redirigirme $nully establecerme igual a $nullmí mismo. Solía ​​preferir enviar contenido [Void], pero eso puede no ser tan comprensible cuando se mira el código o para nuevos usuarios.

Supongo que prefiero ligeramente redirigir la salida a $null.

Do-Something > $null

Editar

Después del comentario de stej nuevamente, decidí hacer algunas pruebas más con tuberías para aislar mejor la sobrecarga de destruir la salida.

Aquí hay algunas pruebas con una simple tubería de 1000 objetos.

## Control Pipeline
Measure-Command {$(1..1000) | ?{$_ -is [int]}}

TotalMilliseconds : 119.3823

## Out-Null
Measure-Command {$(1..1000) | ?{$_ -is [int]} | Out-Null}

TotalMilliseconds : 190.2193

## Redirect to $null
Measure-Command {$(1..1000) | ?{$_ -is [int]} > $null}

TotalMilliseconds : 119.7923

En este caso, Out-Nulltiene una sobrecarga del 60% y > $nulluna sobrecarga del 0.3%.

Anexo 2017-10-16: Originalmente pasé por alto otra opción con Out-Nullel uso del -inputObjectparámetro. Al usar esto, la sobrecarga parece desaparecer, sin embargo, la sintaxis es diferente:

Out-Null -inputObject ($(1..1000) | ?{$_ -is [int]})

Y ahora para algunas pruebas con una simple tubería de 100 objetos.

## Control Pipeline
Measure-Command {$(1..100) | ?{$_ -is [int]}}

TotalMilliseconds : 12.3566

## Out-Null
Measure-Command {$(1..100) | ?{$_ -is [int]} | Out-Null}

TotalMilliseconds : 19.7357

## Redirect to $null
Measure-Command {$(1..1000) | ?{$_ -is [int]} > $null}

TotalMilliseconds : 12.8527

Aquí nuevamente Out-Nulltiene aproximadamente un 60% de gastos generales. Mientras que > $nulltiene una sobrecarga de aproximadamente el 4%. Los números aquí variaron un poco de una prueba a otra (corrí cada uno unas 5 veces y elegí el término medio). Pero creo que muestra una razón clara para no usar Out-Null.

JasonMArcher
fuente
1
i suelen utilizar nulo por las cosas que son parte de una línea de idiomas, y fuera nulo si su ya una larga tubería grande de todos modos
klumsy
25
Out-Nulles tal vez por encima. Pero ... si canaliza un objeto a Out-Null0.076 milisegundos, en mi opinión, todavía está perfectamente bien para el lenguaje de scripting :)
stej
1
Estaba jugando un poco con esto, y obtengo el mejor rendimiento sobre [void] y> $ null usando Out-Null -InputObject ($ (1..1000) |? {$ _ -Is [int]}).
tomohulk
1
Buen punto, eso parece no tener sobrecarga notable.
JasonMArcher
55
Si bien siempre es bueno tener puntos de referencia para comparar diferentes formas de lograr algo, no pasemos por alto la otra conclusión que se puede extraer aquí: a menos que tenga que descartar valores cientos de miles de veces, las diferencias de rendimiento son insignificantes. Y si un milisegundo aquí o allá realmente le importa, probablemente no debería usar PowerShell en primer lugar. Sin hacer un juicio sobre cómo se clasifican las opciones en este sentido, no es necesariamente malo sacrificar la velocidad por otras cualidades, como la legibilidad y la expresión de la intención del programador.
BACON
22

Me doy cuenta de que este es un hilo viejo, pero para aquellos que toman la respuesta aceptada de @ JasonMArcher como un hecho, me sorprende que no se haya corregido, muchos de nosotros lo hemos sabido durante años, en realidad es la TUBERÍA agregando el retraso y NADA que ver con si es Out-Null o no. De hecho, si ejecuta las pruebas a continuación, verá rápidamente que el mismo lanzamiento "más rápido" a [void] y $ void = que durante años todos usamos pensando que era más rápido, en realidad son TAN LENTOS y de hecho MUY LENTOS cuando agrega CUALQUIER canalización de ningún tipo. En otras palabras, tan pronto como conectas algo, toda la regla de no usar out-null entra en la basura.

Prueba, las últimas 3 pruebas en la lista a continuación. El horrible Out-null fue de 32339.3792 milisegundos, pero espera: ¿cuánto más rápido fue lanzar a [void]? 34121.9251 ms?!? WTF? Estos son números REALES en mi sistema, transmitir a VOID fue en realidad MÁS LENTO. ¿Qué tal = $ nulo? 34217.685ms ..... ¡aún más lento! Entonces, como muestran las últimas tres pruebas simples, el Out-Null es en realidad MÁS RÁPIDO en muchos casos cuando la tubería ya está en uso.

Entonces, ¿por qué es esto? Sencillo. Es y siempre fue 100% una alucinación que la tubería a Out-Null fue más lenta. Sin embargo, es que TUBERÍA A CUALQUIER COSA es más lento, ¿y no lo sabíamos ya por lógica básica? Es posible que no sepamos MUCHO más lento, pero estas pruebas cuentan una historia sobre el costo del uso de la tubería si puede evitarlo. Y, en realidad, no estábamos 100% equivocados porque hay un número muy PEQUEÑO de escenarios verdaderos donde el nulo es malo. ¿Cuando? Al agregar Out-Null se agrega la ÚNICA actividad de canalización. En otras palabras ... la razón de un comando simple como $ (1..1000) | Out-Null como se muestra arriba mostró cierto.

Si simplemente agrega una tubería adicional a Out-String en cada prueba anterior, los # cambian radicalmente (o simplemente pegan los siguientes) y, como puede ver por usted mismo, Out-Null en realidad se vuelve MÁS RÁPIDO en muchos casos:

$GetProcess = Get-Process

# Batch 1 - Test 1 
(Measure-Command { 
for ($i = 1; $i -lt 99; $i++) 
{ 
$GetProcess | Out-Null 
} 
}).TotalMilliseconds

# Batch 1 - Test 2 
(Measure-Command { 
for ($i = 1; $i -lt 99; $i++) 
{ 
[void]($GetProcess) 
} 
}).TotalMilliseconds

# Batch 1 - Test 3 
(Measure-Command { 
for ($i = 1; $i -lt 99; $i++) 
{ 
$null = $GetProcess 
} 
}).TotalMilliseconds

# Batch 2 - Test 1 
(Measure-Command { 
for ($i = 1; $i -lt 99; $i++) 
{ 
$GetProcess | Select-Object -Property ProcessName | Out-Null 
} 
}).TotalMilliseconds

# Batch 2 - Test 2 
(Measure-Command { 
for ($i = 1; $i -lt 99; $i++) 
{ 
[void]($GetProcess | Select-Object -Property ProcessName ) 
} 
}).TotalMilliseconds

# Batch 2 - Test 3 
(Measure-Command { 
for ($i = 1; $i -lt 99; $i++) 
{ 
$null = $GetProcess | Select-Object -Property ProcessName 
} 
}).TotalMilliseconds

# Batch 3 - Test 1 
(Measure-Command { 
for ($i = 1; $i -lt 99; $i++) 
{ 
$GetProcess | Select-Object -Property Handles, NPM, PM, WS, VM, CPU, Id, SI, Name | Out-Null 
} 
}).TotalMilliseconds

# Batch 3 - Test 2 
(Measure-Command { 
for ($i = 1; $i -lt 99; $i++) 
{ 
[void]($GetProcess | Select-Object -Property Handles, NPM, PM, WS, VM, CPU, Id, SI, Name ) 
} 
}).TotalMilliseconds

# Batch 3 - Test 3 
(Measure-Command { 
for ($i = 1; $i -lt 99; $i++) 
{ 
$null = $GetProcess | Select-Object -Property Handles, NPM, PM, WS, VM, CPU, Id, SI, Name 
} 
}).TotalMilliseconds

# Batch 4 - Test 1 
(Measure-Command { 
for ($i = 1; $i -lt 99; $i++) 
{ 
$GetProcess | Out-String | Out-Null 
} 
}).TotalMilliseconds

# Batch 4 - Test 2 
(Measure-Command { 
for ($i = 1; $i -lt 99; $i++) 
{ 
[void]($GetProcess | Out-String ) 
} 
}).TotalMilliseconds

# Batch 4 - Test 3 
(Measure-Command { 
for ($i = 1; $i -lt 99; $i++) 
{ 
$null = $GetProcess | Out-String 
} 
}).TotalMilliseconds
Collin Chaffin
fuente
+1 por hacer este punto importante. De hecho, no hay nada que obligue a nadie a usar Out-Nulluna tubería en absoluto, por lo que la mejor manera de mostrar la sobrecarga de una tubería es invocar Out-Nullcon y sin ella. En mi sistema, por 10,000 iteraciones obtengo 0.576 segundos por Out-Null -InputObject $GetProcessvs. 5.656 segundos (casi 10 veces más lento) $GetProcess | Out-Null.
BACON
Hola Collin, gracias por tu respuesta. Analicé sus muestras, pero en todos los lotes [void]y $nullaún obtuve mejores resultados | Out-Null. Entiendo que esto se debe a la tubería y el delta se reduce con los lotes posteriores, pero en mi máquina Out-Nullno funciona más rápido en ninguno de los lotes.
Hinek
Pero, por supuesto, tengo que estar de acuerdo con aquellos que argumentan que el rendimiento no lo es todo y que la legibilidad también debería ser un factor.
Hinek
@Hinek Tu comentario sugiere que no entendiste lo que decía Collin. Sí, [void]y $nullfuncionará mejor que | Out-Null- debido a |. Intenta hacer Out-Null -InputObject (expression)una comparación.
Jonathan Gilbert
19

También está el Out-Nullcmdlet, que puede usar en una tubería, por ejemplo Add-Item | Out-Null.

Página del manual para Out-Null

NAME
    Out-Null

SYNOPSIS
    Deletes output instead of sending it to the console.


SYNTAX
    Out-Null [-inputObject <psobject>] [<CommonParameters>]


DETAILED DESCRIPTION
    The Out-Null cmdlet sends output to NULL, in effect, deleting it.


RELATED LINKS
    Out-Printer
    Out-Host
    Out-File
    Out-String
    Out-Default

REMARKS
     For more information, type: "get-help Out-Null -detailed".
     For technical information, type: "get-help Out-Null -full".
Ocaso Protal
fuente
¿Qué opinas sobre los resultados de la pequeña prueba de referencia en la respuesta de Jason?
Hinek
Muy interesante, especialmente la gran diferencia entre Out-Null y los otros métodos. Creo que cambiaré a [void]pesar de que la solución Out-Null parece más "poderosa".
Ocaso Protal
Todavía no estoy seguro, cuál es mi forma preferida. Incluso esta gran diferencia parece apenas significativa, como ya comentó Stej.
Hinek
2
@Hinek se [void]ve muy claro (aunque no es tan potente como dije), verá al comienzo de la línea que no hay salida en esta línea. Entonces, esta es otra ventaja, y si haces un Out-Null en un bucle grande, el rendimiento podría ser un problema;)
Ocaso Protal
11

Consideraría usar algo como:

function GetList
{
  . {
     $a = new-object Collections.ArrayList
     $a.Add(5)
     $a.Add('next 5')
  } | Out-Null
  $a
}
$x = GetList

La salida de $a.Addno se devuelve, lo que se aplica a todas las $a.Addllamadas a métodos. De lo contrario, deberá anteponer [void]antes de cada llamada.

En casos simples, preferiría [void]$a.Addporque está bastante claro que la salida no se usará y se descartará.

stej
fuente
2

Personalmente, lo uso ... | Out-Nullporque, como han comentado otros, parece el enfoque más "PowerShellish" en comparación con ... > $nully [void] .... $null = ...está explotando un específico variable automática y puede ser fácil pasarlo por alto, mientras que los otros métodos lo hacen obvio con una sintaxis adicional que tiene la intención de descartar la salida de una expresión. Porque ... | Out-Nully ... > $nullal final de la expresión, creo que se comunican efectivamente "toma todo lo que hemos hecho hasta este momento y tíralo", además puedes comentarlos más fácilmente para propósitos de depuración (por ejemplo ... # | Out-Null), en comparación con poner $null =o [void] antes la expresión para determinar qué sucede después de ejecutarlo.

Sin embargo, echemos un vistazo a un punto de referencia diferente: no la cantidad de tiempo que lleva ejecutar cada opción, sino la cantidad de tiempo que lleva averiguar qué hace cada opción . Después de haber trabajado en entornos con colegas que no tenían experiencia con PowerShell o incluso con secuencias de comandos en absoluto, tiendo a tratar de escribir mis secuencias de comandos de una manera que alguien que viene años después podría no entender el lenguaje que está viendo puede tener lucha contra la posibilidad de descubrir qué está haciendo, ya que podrían estar en una posición de tener que apoyarlo o reemplazarlo. Esto nunca se me ha ocurrido como una razón para usar un método sobre los otros hasta ahora, pero imagina que estás en esa posición y usas el helpcomando o tu motor de búsqueda favorito para tratar de averiguar quéOut-Nullhace. Obtienes un resultado útil de inmediato, ¿verdad? Ahora intenta hacer lo mismo con [void]y $null =. No es tan fácil, ¿verdad?

De acuerdo, suprimir la salida de un valor es un detalle bastante menor en comparación con la comprensión de la lógica general de un script, y solo puede tratar de "tontar" tanto su código antes de cambiar su capacidad de escribir un buen código para un la capacidad del principiante para leer ... código no tan bueno. Mi punto es que es posible que algunos que dominan PowerShell ni siquiera estén familiarizados con ellos [void], $null =etc., y solo porque esos se ejecuten más rápido o tomen menos teclas para escribir, no significa que sean la mejor manera de hacerlo. lo que está tratando de hacer, y solo porque un lenguaje le brinde una sintaxis peculiar no significa que deba usarlo en lugar de algo más claro y mejor conocido. * *

* Supongo que Out-Nulles claro y bien conocido, lo que no sé $true. Cualquiera que sea ​​la opción que considere más clara y accesible para los futuros lectores y editores de su código (incluido usted mismo), independientemente de la hora de escribir o de ejecutar, esa es la opción que le recomiendo que use.

TOCINO
fuente