Jaki jest lepszy (czystszy) sposób ignorowania danych wyjściowych w programie PowerShell? [Zamknięte]

134

Załóżmy, że masz metodę lub polecenie cmdlet, które coś zwraca, ale nie chcesz go używać i nie chcesz tego wyprowadzać. Znalazłem te dwa sposoby:

Add-Item > $null

[void]Add-Item

Add-Item | Out-Null

Czego używasz? Jakie jest lepsze / czystsze podejście? Czemu?

Hinek
źródło
1
jak wersja [void] ... jeszcze nie dla mnie, ale spróbuję to zapamiętać.
Masyw

Odpowiedzi:

181

Właśnie przeprowadziłem testy czterech opcji, o których wiem.

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

Więc sugerowałbym, abyś używał czegokolwiek, ale z Out-Nullpowodu kosztów ogólnych. Następną ważną rzeczą byłaby dla mnie czytelność. Lubię przekierowywać do siebie $nulli stawiać $nullsobie równe . Wolę rzucać[Void] , ale może to nie być tak zrozumiałe, gdy patrzę na kod lub dla nowych użytkowników.

Wydaje mi się, że wolę przekierowywać wyjście do $null.

Do-Something > $null

Edytować

Po ponownym komentarzu steja postanowiłem przeprowadzić więcej testów z potokami, aby lepiej odizolować obciążenie związane z usuwaniem danych wyjściowych.

Oto kilka testów z prostym potokiem obiektów 1000.

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

W tym przypadku Out-Nullma około 60% narzut i > $nullokoło 0,3% narzut.

Dodatek 2017-10-16: Pierwotnie przeoczyłem inną opcję z Out-Nullużyciem -inputObjectparametru. Korzystając z tego, narzut wydaje się znikać, jednak składnia jest inna:

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

A teraz kilka testów z prostym potokiem 100 obiektów.

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

Tutaj znowu Out-Nullma około 60% narzutu. Chociaż > $nullma narzut około 4%. Liczby tutaj różniły się nieco od testu do testu (przebiegłem każdy z nich około 5 razy i wybrałem środek). Ale myślę, że to pokazuje jasny powód, aby nie używać Out-Null.

JasonMArcher
źródło
1
Zwykle używam VOID do rzeczy, które są częścią linii językowej i poza wartością zerową, jeśli i tak jest to już duży, długi potok
niezdarny
25
Out-Nullmoże być nad głową. Ale ... jeśli potokujesz jeden obiekt do Out-Null0,076 milisekundy, imho, to nadal jest w porządku dla języka skryptowego :)
stej
1
Trochę się z tym pogubiłem i uzyskam najlepszą wydajność w porównaniu z [void] i> $ null, używając Out-Null -InputObject ($ (1..1000) |? {$ _ -Is [int]}).
tomohulk
1
Słuszna uwaga, wydaje się, że nie ma zauważalnego obciążenia.
JasonMArcher
5
Chociaż zawsze dobrze jest mieć wzorce porównujące różne sposoby osiągnięcia czegoś, nie pomijajmy innego wniosku, który można wyciągnąć tutaj: chyba że musisz odrzucić wartości setki tysięcy razy, gdy różnice w wydajności są nieistotne. A jeśli milisekunda tu lub tam naprawdę ma dla Ciebie znaczenie, prawdopodobnie nie powinieneś używać PowerShell w pierwszej kolejności. Bez oceniania pozycji opcji pod tym względem niekoniecznie jest złą rzeczą poświęcanie szybkości na rzecz innych cech, takich jak czytelność i wyrażanie zamiarów programisty.
BACON
22

Zdaję sobie sprawę, że jest to stary wątek, ale dla tych, którzy przyjmują powyższą odpowiedź @ JasonMArchera za fakt, jestem zaskoczony, że nie została poprawiona, wielu z nas wiedziało od lat, że jest to tak naprawdę PIPELINE dodający opóźnienie i NIC w jest Out-Null czy nie. W rzeczywistości, jeśli uruchomisz poniższe testy, szybko zobaczysz, że te same „szybsze” rzucanie do [void] i $ void = które przez lata myśleliśmy, że było szybsze, są właściwie TAKIE WOLNE, a tak naprawdę BARDZO WOLNE, gdy dodasz JAKIEKOLWIEK rurociągi. Innymi słowy, gdy tylko do czegokolwiek potokujesz, cała zasada nieużywania out-null trafia do kosza.

Dowód, ostatnie 3 testy z poniższej listy. Straszna wartość Out-null wynosiła 32339,3792 milisekund, ale czekaj - o ile szybsze było rzucanie do [void]? 34121,9251 ms?!? WTF? To są PRAWDZIWE # w moim systemie, przesyłanie do VOID było w rzeczywistości wolniejsze. A co powiesz na = $ null? 34217.685ms ..... nadal cholernie wolniej! Tak więc, jak pokazują ostatnie trzy proste testy, wartość Out-Null jest w rzeczywistości SZYBSZA w wielu przypadkach, gdy potok jest już używany.

Więc dlaczego tak się dzieje? Prosty. To jest i zawsze było w 100% halucynacją, że połączenie z Out-Null było wolniejsze. Chodzi jednak o to, że RUROWANIE DO WSZYSTKIEGO jest wolniejsze i czy nie wiedzieliśmy już o tym za pomocą podstawowej logiki? Po prostu możemy nie wiedzieć, JAK DUŻO wolniej, ale te testy z pewnością opowiadają historię o koszcie korzystania z potoku, jeśli można go uniknąć. I nie myliliśmy się w 100%, ponieważ istnieje bardzo MAŁA liczba prawdziwych scenariuszy, w których wartość zerowa jest zła. Kiedy? Podczas dodawania Out-Null dodaje się TYLKO działanie potoku. Innymi słowy… powodem jest proste polecenie, takie jak $ (1..1000) | Out-Null, jak pokazano powyżej, okazało się prawdą.

Jeśli po prostu dodasz dodatkową potokę do Out-String do każdego powyższego testu, #s zmieniają się radykalnie (lub po prostu wklejasz te poniżej) i, jak sam widzisz, Out-Null faktycznie staje się SZYBSZY w wielu przypadkach:

$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
źródło
+1 za uczynienie tego ważnego punktu. W rzeczywistości nic nie zmusza nikogo do używania Out-Nullpotoku, więc najlepszym sposobem pokazania narzutu potoku jest wywołanie Out-Nullz nim i bez niego. W moim systemie przy 10000 iteracji otrzymuję 0,576 sekundy w Out-Null -InputObject $GetProcessporównaniu z 5,656 sekundy (prawie 10 razy wolniej) $GetProcess | Out-Null.
BACON
Cześć Collin, dziękuję za odpowiedź. Sprawdziłem twoje próbki, ale we wszystkich partiach [void]i $nullnadal osiągałem lepsze niż | Out-Null. Rozumiem, że Out-Nulldzieje się tak z powodu rurociągu i delta kurczy się wraz z późniejszymi partiami, ale na moim komputerze nie działa szybciej w żadnej z partii.
Hinek
Ale oczywiście muszę zgodzić się z tymi, którzy twierdzą, że wydajność to nie wszystko, a czytelność też powinna mieć znaczenie.
Hinek
@Hinek Twój komentarz sugeruje, że przegapiłeś to, o czym mówił Collin. Tak, [void]i $nullbędzie działać lepiej niż | Out-Null- dzięki |. Spróbuj Out-Null -InputObject (expression)dla porównania.
Jonathan Gilbert
19

Istnieje również Out-Nullcmdlet, którego można użyć na przykład w potoku Add-Item | Out-Null.

Strona podręcznika dla 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
źródło
Co myślisz o wynikach małego testu porównawczego w odpowiedzi Jasona?
Hinek
Bardzo interesujące, zwłaszcza naprawdę ogromna różnica między Out-Null a innymi metodami. Myślę, że przejdę na, [void]mimo że rozwiązanie Out-Null wygląda bardziej „powershellish”.
Ocaso Protal
Nadal nie jestem pewien, jaki jest mój ulubiony sposób. Nawet ta ogromna różnica wydaje się mało znacząca, jak już stej skomentował.
Hinek,
2
@Hinek [void]wygląda bardzo przejrzyście (choć nie jest w Powerhellish, jak powiedziałem), zobaczysz na początku wiersza, że ​​nie ma wyjścia w tym wierszu. Więc to jest kolejna zaleta, a jeśli zrobisz Out-Null w dużej pętli, wydajność może być problemem;)
Ocaso Protal
11

Rozważałbym użycie czegoś takiego:

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

Dane wyjściowe z $a.Addnie są zwracane - dotyczy to wszystkich $a.Addwywołań metod. W przeciwnym razie musiałbyś poprzedzać[void] przed każdym połączeniem .

W prostych przypadkach wybrałbym, [void]$a.Addponieważ jest całkiem jasne, że wynik nie zostanie wykorzystany i zostanie odrzucony.

stej
źródło
2

Osobiście używam, ... | Out-Nullponieważ, jak zauważyli inni, wygląda to na bardziej "PowerShellish" podejście w porównaniu z ... > $nulli [void] .... $null = ...wykorzystuje określoną automatyczną zmienną i można ją łatwo przeoczyć, podczas gdy inne metody za pomocą dodatkowej składni jasno pokazują, że zamierzasz odrzucić dane wyjściowe wyrażenia. Ponieważ ... | Out-Nulli ... > $nullna końcu wyrażenia, myślę, że skutecznie komunikują się "weź wszystko, co zrobiliśmy do tego momentu i wyrzuć to", a ponadto możesz je łatwiej komentować w celach debugowania (np. ... # | Out-Null), W porównaniu do wstawiania $null =lub [void] wcześniej wyrażenie określające, co dzieje się po jego wykonaniu.

Spójrzmy jednak na inny test porównawczy: nie ilość czasu potrzebnego do wykonania każdej opcji, ale czas potrzebny na ustalenie , co robi każda opcja . Pracując w środowiskach z kolegami, którzy nie mieli doświadczenia z PowerShell, a nawet w ogóle nie pisali skryptów, staram się pisać moje skrypty w taki sposób, że ktoś, kto pojawi się po latach i może nawet nie rozumieć języka, na który patrzy, może mieć walcząc z szansą na ustalenie, co robi, ponieważ mogą być w sytuacji, gdy będą musieli go wspierać lub zastępować. Nigdy nie przyszło mi to do głowy jako powód, by do tej pory używać jednej metody zamiast innych, ale wyobraź sobie, że jesteś w takiej sytuacji i używasz helppolecenia lub ulubionej wyszukiwarki, aby dowiedzieć się, coOut-Nullrobi. Natychmiast uzyskasz użyteczny wynik, prawda? Teraz spróbuj zrobić to samo z [void]i $null =. Nie takie proste, prawda?

To prawda, pomijanie wartości wyjściowej jest dość drobnym szczegółem w porównaniu do zrozumienia ogólnej logiki skryptu, a możesz tylko próbować „ogłupić” swój kod tak bardzo, zanim zamienisz umiejętność napisania dobrego kodu na umiejętność czytania ... niezbyt dobrego kodu przez nowicjusza. Chodzi mi o to, że to możliwe, że tacy, którzy biegle w PowerShell nawet nie zna [void], $null =itd, i właśnie dlatego te może wykonać szybciej lub wziąć mniej klawiszy do typu, nie oznacza, że są najlepszym sposobem aby to zrobić co próbujesz zrobić, a tylko dlatego, że język ma dziwaczną składnię, nie oznacza, że ​​powinieneś go używać zamiast czegoś jaśniejszego i lepiej znanego. *

* Przypuszczam, że Out-Nulljest to jasne i dobrze znane, czego nie wiem $true. Niezależnie od opcji, możesz czuć się najczystsze i najbardziej dostępne dla czytelników i redaktorów kodzie przyszłych (siebie w zestawie), niezależnie od czasu do rodzaju i czasu do wykonania, to opcja ja polecając skorzystać.

BOCZEK
źródło