Jaka jest różnica między „Write-Host”, „Write-Output” lub „[console] :: WriteLine”?

192

Istnieje wiele różnych sposobów wysyłania komunikatów. Jaka jest różnica pomiędzy skuteczne wyprowadzanie czegoś poprzez Write-Host, Write-Outputlub [console]::WriteLine?

Zauważam również, że jeśli użyję:

write-host "count=" + $count

+Zostanie dołączona do wyjścia. Dlaczego tak jest Czy nie należy oceniać wyrażenia w celu utworzenia pojedynczego połączonego łańcucha przed jego zapisaniem?

Scott Langham
źródło
4
Write-Outputkiedy emitujesz wyniki. Write-Hostpodczas wysyłania informacji o logowaniu. Nigdy nie należy używać [console]::writeline().
JohnL,
2
@JohnL dlaczego nigdy nie powinniśmy używać [console] :: writeline ()?
David Klempfner,
3
@Backwards_Dave Ponieważ masz Host-Write .... Ok, mogłem mieć pewne wrażenie, że pokazało nowe okno konsoli (to było dość dawno temu). To się nie zdarza, ale faktem jest, że to nie jest idiom powłoki PowerShell i nic nie możesz z [console]::writeline("hello world")tym zrobić Write-Host "hello world". Inną, lepszą, ostatnio stosowaną odpowiedzią jest write-hostowijanie, write-informationdzięki czemu jego dane są umieszczane w strumieniu, write-errortak abyś mógł je przechwycić i wykorzystać w innym miejscu. [console]::writeline()nie robi tego
JohnL

Odpowiedzi:

267

Write-Outputpowinien być używany, gdy chcesz wysłać dane w linii potoku, ale niekoniecznie chcesz je wyświetlić na ekranie. Potok ostatecznie zapisze go, out-defaultjeśli nic innego go nie użyje wcześniej.

Write-Host należy stosować, gdy chcesz zrobić odwrotnie.

[console]::WriteLinejest zasadniczo tym, co Write-Hostrobi za kulisami.

Uruchom ten kod demonstracyjny i sprawdź wynik.

function Test-Output {
    Write-Output "Hello World"
}

function Test-Output2 {
    Write-Host "Hello World" -foreground Green
}

function Receive-Output {
    process { Write-Host $_ -foreground Yellow }
}

#Output piped to another function, not displayed in first.
Test-Output | Receive-Output

#Output not piped to 2nd function, only displayed in first.
Test-Output2 | Receive-Output 

#Pipeline sends to Out-Default at the end.
Test-Output 

Musisz zawrzeć operację konkatenacji w nawiasach, aby program PowerShell przetworzył konkatenację przed tokenizacją listy parametrów Write-Hostlub użyj interpolacji łańcuchów

write-host ("count=" + $count)
# or
write-host "count=$count"

BTW - Obejrzyj wideo Jeffreya Snovera wyjaśniającego, jak działa rurociąg. Kiedy zacząłem uczyć się programu PowerShell, znalazłem to najbardziej przydatne wyjaśnienie działania potoku.

Andy Arismendi
źródło
W Azure WebJob [konsola] :: WriteLine działa, ale Host zapisu spowoduje błąd: Błąd wewnętrzny Win32 „Uchwyt jest nieprawidłowy” 0x6 wystąpił podczas ustawiania atrybutów znaków dla bufora wyjściowego konsoli. Nie pytaj mnie dlaczego.
Gil Roitto
Popraw mnie, jeśli się mylę, ale uważam, że Write-Outputjest domyślny, np. Jeśli masz PsObject i po prostu wyplujesz go na ekran, robiąc to $object, faktycznie zrobi to samo Write-Output $object. Warto wspomnieć
Kanion Kolob
1
Pojawiły się nowe wytyczne: unikaj korzystania, Write-Outputgdy to możliwe. Zobacz github.com/PoshCode/PowerShellPracticeAndStyle/issues/… > Użyj return tylko do zakończenia wykonywania. > Unikaj zapisu / zapisu (...). Zamiast tego, jeśli chcesz, aby dane wyjściowe były wyraźniejsze, po prostu przypisz dane wyjściowe do odpowiednio nazwanej zmiennej. i umieścił tę zmienną w linii, aby zasygnalizować jawne wyjście. > Write-Output -NoEnumerate jest uszkodzony w PowerShell 6 i działa już od 16 miesięcy lub dłużej - nie ma planu, aby to naprawić. Podsumowując:> NIE UŻYWAJ Write-Output - EVER.
Jeroen Wiert Pluimers
28

Oprócz tego, o czym wspomniał Andy, istnieje jeszcze jedna różnica, która może być ważna - write-host bezpośrednio zapisuje na hoście i nic nie zwraca, co oznacza, że ​​nie można przekierować wyjścia, np. Do pliku.

---- script a.ps1 ----
write-host "hello"

Teraz uruchom w PowerShell:

PS> .\a.ps1 > someFile.txt
hello
PS> type someFile.txt
PS>

Jak widać, nie można przekierować ich do pliku. Może to być zaskakujące dla kogoś, kto nie jest ostrożny.

Ale jeśli zostanie przełączony na użycie zapisu / wyjścia, przekierowanie będzie działało zgodnie z oczekiwaniami.

KFL
źródło
Możliwe jest przechwycenie danych wyjściowych Write-Host, jeśli używasz programu PowerSet Start-Process. \ A.ps1 -RedirectStandardOutput somefile.txt Występują jednak problemy z kodowaniem (plik będzie w SystemDefaultEncoding).
MKesper
16

Oto inny sposób na osiągnięcie odpowiednika zapisu / wyjścia. Wystarczy wpisać ciąg w cudzysłów:

"count=$count"

Możesz upewnić się, że działa to tak samo jak Zapis-Wyjście, uruchamiając ten eksperyment:

"blah blah" > out.txt

Write-Output "blah blah" > out.txt

Write-Host "blah blah" > out.txt

Pierwsze dwa wypiszą „bla bla” na out.txt, ale trzeci nie.

„help Write-Output” daje wskazówkę dotyczącą tego zachowania:

To polecenie cmdlet jest zwykle używane w skryptach do wyświetlania ciągów i innych obiektów w konsoli. Ponieważ jednak domyślnym zachowaniem jest wyświetlanie obiektów na końcu potoku, zwykle nie jest konieczne użycie polecenia cmdlet.

W takim przypadku sam ciąg „count = $ count” jest obiektem na końcu potoku i jest wyświetlany.

FarmerBob
źródło
6

Dla zastosowań Write-Host, PSScriptAnalyzerprodukuje następującą diagnostykę:

Unikaj używania, Write-Hostponieważ może nie działać na wszystkich hostach, nie działa, gdy nie ma hosta i (przed PS 5.0) nie można go stłumić, przechwycić ani przekierować. Zamiast tego, stosowanie Write-Output, Write-Verboselub Write-Information.

Więcej informacji znajduje się w dokumentacji tej reguły. Fragmenty dla potomności:

Używanie Write-Hostjest bardzo odradzane, chyba że w użyciu komend z Showczasownikiem. ShowCzasownik oznacza „wyraźnie pokazać na ekranie, bez żadnych innych możliwości”.

Polecenia z Showczasownikiem nie mają tej kontroli zastosowanej.

Jeffrey Snover ma wpis na blogu „ Host-host” uznany za szkodliwy, w którym twierdzi, że „ Host-host” jest prawie zawsze niewłaściwy, ponieważ zakłóca automatyzację i zapewnia więcej wyjaśnień związanych z diagnostyką, jednak powyższe jest dobrym podsumowaniem.

Drew Noakes
źródło
3

Z moich testów Write-Output i [Console] :: WriteLine () działają znacznie lepiej niż Write-Host.

W zależności od tego, ile tekstu trzeba napisać, może to być ważne.

Poniżej, jeśli wynik 5 testów dla Write-Host, Write-Output i [Console] :: WriteLine ().

Z mojego ograniczonego doświadczenia wynika, że ​​podczas pracy z danymi rzeczywistymi muszę porzucić polecenia cmdlet i przejść bezpośrednio do poleceń niższego poziomu, aby uzyskać przyzwoitą wydajność ze skryptów.

measure-command {$count = 0; while ($count -lt 1000) { Write-Host "hello"; $count++ }}

1312ms
1651ms
1909ms
1685ms
1788ms


measure-command { $count = 0; while ($count -lt 1000) { Write-Output "hello"; $count++ }}

97ms
105ms
94ms
105ms
98ms


measure-command { $count = 0; while ($count -lt 1000) { [console]::WriteLine("hello"); $count++ }}

158ms
105ms
124ms
99ms
95ms
Tomek
źródło
1
Write-Outputi Write-Hostsłużą różnym celom, więc nie ma sensu porównywać ich wydajności. Tak, bezpośrednie użycie typów .NET i ich metod jest szybsze, ale brakuje Ci funkcji wyższego poziomu, które mogą zapewnić cmdlety PowerShell. [Console]::WriteLine()jest podobny Write-Hostw omawianej sprawie, ale nie będzie działać w każdych okolicznościach, ponieważ nie wszystkie hosty PowerShell są konsolami .
mklement0
0

Odnośnie [Console] :: WriteLine () - powinieneś go użyć, jeśli zamierzasz używać potoków w CMD (nie w PowerShell). Załóżmy, że chcesz, aby Twój PS1 przesyłał strumieniowo wiele danych do standardowego wyjścia, a także inne narzędzie do ich konsumpcji / transformacji. Jeśli użyjesz Write-Host w skrypcie, będzie on znacznie wolniejszy.

Mike Twc
źródło