Was ist der bessere (sauberere) Weg, um die Ausgabe in PowerShell zu ignorieren? [geschlossen]

134

Angenommen, Sie haben eine Methode oder ein Cmdlet, das etwas zurückgibt, aber Sie möchten es nicht verwenden und Sie möchten es nicht ausgeben. Ich habe diese zwei Wege gefunden:

Add-Item > $null

[void]Add-Item

Add-Item | Out-Null

Was benutzt du? Welches ist der bessere / sauberere Ansatz? Warum?

Hinek
quelle
1
wie die [nichtige] Version ... war noch nicht für mich, aber ich werde versuchen, mich daran zu erinnern.
Massiv

Antworten:

181

Ich habe gerade einige Tests der vier Optionen durchgeführt, die ich kenne.

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

Daher würde ich vorschlagen, dass Sie alles andere als Out-Nullaufgrund von Overhead verwenden. Das nächste wichtige für mich wäre die Lesbarkeit. Ich mag es, $nullzu $nullmir selbst umzuleiten und mich selbst gleichzustellen . Ich bevorzuge Casting [Void], aber das ist möglicherweise nicht so verständlich, wenn ich auf Code oder für neue Benutzer schaue.

Ich schätze, ich bevorzuge es etwas, die Ausgabe umzuleiten $null.

Do-Something > $null

Bearbeiten

Nach dem erneuten Kommentar von stej entschied ich mich, weitere Tests mit Pipelines durchzuführen, um den Aufwand für die Müllabfuhr der Ausgabe besser zu isolieren.

Hier sind einige Tests mit einer einfachen 1000-Objekt-Pipeline.

## 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

In diesem Fall Out-Nullhat ein Overhead von ungefähr 60% und > $nulleinen Overhead von ungefähr 0,3%.

Nachtrag 2017-10-16: Ich habe ursprünglich eine andere Option mit Out-Nullder Verwendung des -inputObjectParameters übersehen . Wenn Sie dies verwenden, scheint der Overhead zu verschwinden, die Syntax ist jedoch anders:

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

Und nun zu einigen Tests mit einer einfachen 100-Objekt-Pipeline.

## 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

Auch hier Out-Nullliegt der Overhead bei ca. 60%. Während > $nullhat einen Overhead von ca. 4%. Die Zahlen hier variierten ein wenig von Test zu Test (ich lief jeweils ungefähr 5 Mal und wählte den Mittelweg). Aber ich denke, es zeigt einen klaren Grund, nicht zu verwenden Out-Null.

JasonMArcher
quelle
1
Ich benutze normalerweise VOID für Dinge, die Teil einer Sprachzeile sind, und out-null, wenn es sowieso schon eine große lange Pipeline ist
klumsy
25
Out-Nullist vielleicht Overhead. Aber ... wenn Sie ein Objekt auf Out-Null0,076 Millisekunden leiten, ist es für die Skriptsprache immer noch vollkommen in Ordnung :)
stej
1
Ich habe ein wenig damit herumgespielt und mit Out-Null -InputObject ($ (1..1000) |? {$ _ -Is [int]}) die beste Leistung über [void] und> $ null erzielt.
Tomohulk
1
Guter Punkt, das scheint keinen merklichen Overhead zu haben.
JasonMArcher
5
Es ist zwar immer gut, Benchmarks zu haben, um verschiedene Wege zu vergleichen, um etwas zu erreichen, aber lassen Sie uns nicht die andere Schlussfolgerung übersehen, die hier gezogen werden kann: Wenn Sie Werte nicht hunderttausendfach verwerfen müssen, sind die Leistungsunterschiede vernachlässigbar. Und wenn Ihnen hier oder da eine Millisekunde wirklich wichtig ist, sollten Sie PowerShell wahrscheinlich gar nicht erst verwenden. Ohne ein Urteil darüber zu fällen, wie die Optionen in dieser Hinsicht rangieren, ist es nicht unbedingt eine schlechte Sache, die Geschwindigkeit für andere Eigenschaften wie Lesbarkeit und Ausdruck der Absicht des Programmierers zu opfern.
Speck
22

Mir ist klar, dass dies ein alter Thread ist, aber für diejenigen, die die oben akzeptierte Antwort von @ JasonMArcher als Tatsache betrachten, bin ich überrascht, dass sie nicht korrigiert wurde. Viele von uns wissen seit Jahren, dass es tatsächlich die PIPELINE ist, die die Verzögerung hinzufügt und NICHTS damit zu tun hat, ob es ist Out-Null oder nicht. Wenn Sie die folgenden Tests ausführen, werden Sie schnell feststellen, dass das gleiche "schnellere" Casting in [void] und $ void = das wir alle jahrelang dachten, es sei schneller, tatsächlich genauso langsam und in der Tat SEHR LANGSAM, wenn Sie fügen JEDES Pipelining hinzu. Mit anderen Worten, sobald Sie zu etwas weiterleiten, wird die gesamte Regel, out-null nicht zu verwenden, in den Papierkorb verschoben.

Beweis, die letzten 3 Tests in der Liste unten. Der schreckliche Out-Null war 32339.3792 Millisekunden, aber warte - wie viel schneller war das Casting auf [void]? 34121.9251 ms?!? WTF? Dies sind ECHTE Nummern auf meinem System. Das Casting auf VOID war tatsächlich langsamer. Wie wäre es mit = $ null? 34217.685ms ..... immer noch verdammt langsamer! Wie die letzten drei einfachen Tests zeigen, ist Out-Null in vielen Fällen SCHNELLER, wenn die Pipeline bereits verwendet wird.

Warum ist das so? Einfach. Es ist und war zu 100% eine Halluzination, dass das Piping zu Out-Null langsamer war. Es ist jedoch so, dass PIPING TO ALLES langsamer ist, und haben wir das nicht schon durch grundlegende Logik gewusst? Wir wissen vielleicht nicht, WIE VIEL langsamer, aber diese Tests erzählen sicher eine Geschichte über die Kosten der Nutzung der Pipeline, wenn Sie dies vermeiden können. Und wir haben uns nicht wirklich zu 100% geirrt, weil es eine sehr kleine Anzahl von wahren Szenarien gibt, in denen Out-Null böse ist. Wann? Beim Hinzufügen von Out-Null wird die EINZIGE Pipeline-Aktivität hinzugefügt. Mit anderen Worten ... der Grund für einen einfachen Befehl wie $ (1..1000) | Out-Null wie oben gezeigt zeigte wahr.

Wenn Sie Out-String einfach zu jedem Test oben eine zusätzliche Pipe hinzufügen, ändern sich die # radikal (oder fügen Sie einfach die folgenden ein), und wie Sie selbst sehen können, wird der Out-Null in vielen Fällen SCHNELLER:

$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
quelle
+1 für diesen wichtigen Punkt. Tatsächlich zwingt niemand jemanden, Out-Nulleine Pipeline zu verwenden. Der beste Weg, um den Overhead einer Pipeline anzuzeigen, besteht darin, sie Out-Nullmit und ohne Pipeline aufzurufen . Auf meinem System erhalte ich für 10.000 Iterationen 0,576 Sekunden Out-Null -InputObject $GetProcessgegenüber 5,656 Sekunden (fast 10x langsamer) für $GetProcess | Out-Null.
Speck
Hallo Collin, danke für deine Antwort. Ich habe Ihre Proben aber in allen Chargen laufen lassen [void]und $nulltrotzdem eine bessere Leistung erbracht als | Out-Null. Ich verstehe, dass dies an der Pipeline liegt und das Delta mit den späteren Chargen schrumpft, aber auf meinem Computer ist Out-Nulldie Leistung in keiner der Chargen schneller.
Hinek
Aber natürlich muss ich denen zustimmen, die argumentieren, dass Leistung nicht alles ist und Lesbarkeit auch berücksichtigt werden sollte.
Hinek
@Hinek Dein Kommentar deutet darauf hin, dass du den Punkt verpasst hast, den Collin gesagt hat. Ja, [void]und $nullwird besser abschneiden als | Out-Null- wegen der |. Versuchen Sie es Out-Null -InputObject (expression)zum Vergleich.
Jonathan Gilbert
19

Es gibt auch das Out-NullCmdlet, das Sie beispielsweise in einer Pipeline verwenden können Add-Item | Out-Null.

Handbuchseite für 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
quelle
Was denkst du über die Ergebnisse des kleinen Benchmark-Tests in Jasons Antwort?
Hinek
Sehr interessant, besonders der wirklich große Unterschied zwischen Out-Null und den anderen Methoden. Ich denke, ich werde zu wechseln, [void]obwohl die Out-Null-Lösung eher "Powershellish" aussieht.
Ocaso Protal
Ich bin mir immer noch nicht sicher, was mein bevorzugter Weg ist. Selbst dieser große Unterschied scheint kaum signifikant zu sein, wie stej bereits kommentierte.
Hinek
2
@Hinek [void]sieht sehr klar aus (obwohl nicht wie gesagt Powershellish), Sie werden am Anfang der Zeile sehen, dass es in dieser Zeile keine Ausgabe gibt. Dies ist also ein weiterer Vorteil, und wenn Sie einen Out-Null in einer großen Schleife
ausführen
11

Ich würde in Betracht ziehen, etwas zu verwenden wie:

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

Die Ausgabe von $a.Addwird nicht zurückgegeben - dies gilt für alle $a.AddMethodenaufrufe. Andernfalls müssten Sie [void]vor jedem Anruf ein Präfix voranstellen .

In einfachen Fällen würde ich mitgehen, [void]$a.Addweil es ziemlich klar ist, dass die Ausgabe nicht verwendet wird und verworfen wird.

stej
quelle
2

Persönlich verwende ich, ... | Out-Nullweil, wie andere kommentiert haben, dies im Vergleich zu ... > $nullund wie der "PowerShellish" -Ansatz aussieht [void] .... $null = ...nutzt eine bestimmte automatische Variable aus und kann leicht übersehen werden, während die anderen Methoden mit zusätzlicher Syntax deutlich machen, dass Sie die Ausgabe eines Ausdrucks verwerfen möchten. Weil ... | Out-Nullund ... > $nullam Ende des Ausdrucks denke ich, dass sie effektiv kommunizieren "nimm alles, was wir bis zu diesem Punkt getan haben und wirf es weg", und du kannst sie für Debugging-Zwecke (z. B. ... # | Out-Null) einfacher auskommentieren als Putten $null =oder [void] vorher der Ausdruck, um zu bestimmen, was nach der Ausführung passiert .

Schauen wir uns jedoch einen anderen Benchmark an: Nicht die Zeit, die zum Ausführen jeder Option benötigt wird, sondern die Zeit, die benötigt wird, um herauszufinden, was jede Option tut . Nachdem ich in Umgebungen mit Kollegen gearbeitet habe, die noch keine Erfahrung mit PowerShell oder Skripten hatten, versuche ich, meine Skripte so zu schreiben, dass jemand, der Jahre später kommt und möglicherweise nicht einmal die Sprache versteht, die er sich ansieht, eine haben kann Kampf gegen die Chance, herauszufinden, was es tut, da sie möglicherweise in der Lage sind, es zu unterstützen oder zu ersetzen. Dies ist mir bisher noch nie als Grund in den Sinn gekommen, eine Methode gegenüber der anderen zu verwenden. Stellen Sie sich jedoch vor, Sie befinden sich in dieser Position und verwenden den helpBefehl oder Ihre bevorzugte Suchmaschine, um herauszufinden, wasOut-Nulltut. Sie erhalten sofort ein nützliches Ergebnis, oder? Versuchen Sie nun, dasselbe mit [void]und zu tun $null =. Nicht so einfach, oder?

Zugegeben, das Unterdrücken der Ausgabe eines Werts ist im Vergleich zum Verständnis der Gesamtlogik eines Skripts ein ziemlich kleines Detail, und Sie können nur versuchen, Ihren Code so sehr zu "dumm" zu machen, bevor Sie Ihre Fähigkeit, guten Code für a zu schreiben, eintauschen Fähigkeit des Anfängers zu lesen ... nicht so guter Code. Mein Punkt ist, es möglich ist , dass einige , die in Powershell fließend sind , sind nicht einmal kennt [void], $null =usw., und nur weil die schneller ausführen kann oder nehmen weniger Tastenanschläge Typ, bedeutet nicht , sie sind der beste Weg , zu tun Was Sie versuchen zu tun, und nur weil eine Sprache Ihnen eine eigenartige Syntax gibt, heißt das nicht, dass Sie sie anstelle von etwas Klarerem und Bekannterem verwenden sollten. * *

* Ich gehe davon aus, dass dies Out-Nullklar und bekannt ist, was ich nicht weiß $true. Unabhängig davon, welche Option Ihrer Meinung nach für zukünftige Leser und Redakteure Ihres Codes (einschließlich Sie selbst) am klarsten und zugänglichsten ist, unabhängig von der Zeit bis zur Eingabe oder der Zeit bis zur Ausführung, empfehle ich Ihnen diese Option.

SPECK
quelle