Write-Host Debugging
Was dem VBScript-Programmierer sein "WScript.Echo" ist, ist für PowerShell das "Write-Host". Einfach mal schnell an verschiedenen Stellen des Skripts eine Bildschirmausgabe auswerfen um damit den Fortschritt des Skripts zu erkennen oder andere Informationen auszugeben. Zudem gibt es noch "Write-Warning" und "Write-Error", um Fehler und Warnungen farblich in guter PowerShell-Manier auszugeben. Wer Skripte unbewacht laufen lässt, wird sich freuen, dass diese Ausgaben mit "Start-Transcript" auch noch in eine Datei ausgegeben werden können. Aber es funktioniert nicht immer und wie wäre ein "besseres Write-Host", welches verschiedene Levels unterscheidet, Meldungen woanders hin sendet etc.
Microsoft nutzt z.B.: im Skript CVE-2023-23397.ps1 (Quelle: https://microsoft.github.io/CSS-Exchange/Security/CVE-2023-23397/) auch den Trick die Funktion "Write-Host" umzuschreiben.
Write-Host, Pipeline und Transcript
Wer heute ein Skript schreibt, sucht natürlich nach einer Möglichkeit zur Ausgabe von Meldungen. Da in PowerShell aber auch vieles über die Pipeline übertragen wird, muss man nun abwägen, ob ich die Ausgaben einfach so auf STDOUT sende. Solche Ausgaben kann ein per Pipeline angeschlossene Skript dann natürlich weiter verarbeiten. Oft ist aber genau das störend und man möchte nur die Ergebnisse in die Pipeline ausgeben. Diagnoseausgaben sollen auch auf der Konsole sichtbar sein aber eben nicht in der Pipeline landen.
Achtung: Seit PowerShell 5 ist "Write-Host" nur ein Alias für Write-Information.
The Write-Host cmdlet customizes output.
You can specify the color of text by using the
ForegroundColor parameter, and you can specify the
background color by using the BackgroundColor parameter. The
Separator parameter lets you specify a string to use to
separate displayed objects. The particular result depends on
the program that is hosting PowerShell. Note Starting in
Windows PowerShell 5.0, Write-Host is a wrapper for
Write-Information This allows you to use Write-Host to emit
output to the information stream. This enables the capture
or suppression of data written using Write-Host while
preserving backwards compatibility. The $InformationPreference
preference variable and InformationAction common parameter
do not affect Write-Host messages.
Quelle: Write-Host
https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/write-host?view=powershell-6
Um das zu testen, habe ich eine kleine PS1-Datei angelegt, die verschiedene Ausgaben generiert.
[CmdletBinding()] param() start-transcript -path c:\temp\transcript.txt $DebugPreference="Continue" write-host "Write-Host" write-verbose "Write-verbose" write-debug "Write-debug" write-error "Write-error" write-host "Write-host" out-host -inputobject "out-host" stop-transcript
Die Ausgabe in der Datei ist zumindest bei mir sehr umfangreich, wenn die die Befehle interaktiv ausführe:
Im Transcript ist hier quasi alles drin, was auch auf der Konsole erschienen ist. Nur die "Verbose-Ausgabe fehlt, da ich diesen nicht als Parameter angegeben hatte. Die gleichen Befehle habe ich dann als PS1-Datei abgelegt und ausgeführt. Da hier die Befehle selbst nicht auf der "Konsole" erscheinen, kommen nur die Ausgaben zum Vorschein.
Hier ist nun auch die Verbose-Ausgabe vorhanden und im Transcript fehlen die eigentlichen Befehle, die auch nicht auf der Konsole erscheinen.
- Receive-Job duplicates Write-Host output
from background job
https://GitHub.com/PowerShell/PowerShell/issues/7814 - Remoting infrastructure / minishells
don't consistently support capturing /
redirection / suppression of streams other
than success and error #958
https://GitHub.com/PowerShell/PowerShell/issues/9585
Write-Host fehlt im Transcript
Auch wenn so erst einmal alles "ordentlich" aussieht, so gibt es dennoch die ein oder anderen Probleme. Ich nutze ja die PowerShell auch für Skripte die automatisch ablaufen. Sie werden z.B. für den Taskplaner gestartet und hier ist mir für ein "Logging" natürlich wichtig, die Ausgaben auch temporär in einer Datei zu protokollieren. Schließlich ist kein Skript "fehlerfrei" und auch wenn ich den Fehler abfange und mit "Exit <Fehlernummer>" dem aufrufenden Task mitgebe, so komme ich dann erst einmal nicht mehr an die Bildschirmausgaben heran.
Daher habe ich bislang immer intensiv ein "Start-Transcript" mit einem Zeitstempel gestartet. Die Zeilen sehen dann wie folgt aus:
[string]$transcriptfile = (".\logs\console."+(get-date -format "yyyy-MM-dd-HH-mm-ss-fff")+".log") write-host "Start Transcript to " $transcriptfile Start-Transcript -path $transcriptfile # hier dann der weitere Code
Natürlich muss ich am Ende das Transcript wieder stoppen und mich auch um das Aufräumen der alten Logs z.B. über das Alter kümmern.
Komischerweise gibt es immer wieder Server, auf denen das Logfile quasi "leer" bleibt. Da steht dann nur folgendes drin:
********************** Windows PowerShell Transcript Start Start time: 20160520152344 Username : MSXFAQ\SVC-JOB Machine : JOBSERVER01 (Microsoft Windows NT 6.1.7601 Service Pack 1) ********************** Transcript started, output file is C:\Tasks\tool1\logs\console.2016-05-20-15-23-44-291.log ********************** Windows PowerShell Transcript End End time: 20160520152505 **********************
Warum das so ist, habe ich bislang noch nicht ermitteln können. Es scheint an der Build-Version von PowerShell zu liegen, denn es gibt wohl Hinweise im Internet über dieses Problem und auch Links auf entsprechende Updates und Hotfixes. Einige Leute berichten, dass es das Windows Update 3000850 war.
„… In my testing I discovered that this
bug only appears after installing KB3000850
I found the issue in KB3000850 since it's a later rollup
that includes everything in KB2919355
https://www.microsoft.com/de-DE/download/details.aspx?id=44975
(700MB, Nov 14)
https://support.microsoft.com/en-us/kb/3000850
Aber das kam schon November 2014 raus und ich habe das
Problem auch im Sommer 2016 wieder gehabt
Dann bin ich auf ein Hotfix vom April 2015 gestoßen.
- 3014136 PowerShell transcript file
doesn't contain the correct information in
Windows Server 2012 R2
https://support.microsoft.com/en-us/kb/3014136
Aber in den Microsoft Foren fand ich auch Aussagen wie:
Oder auch „This is how PowerShell is
designed to work. Output is only visible in the console.“
Oder “A similar issue is happening on powersherll 5, Windows
Server 2008 R2. When the write-host comes from a remote
machine it dones't works:”
Write-Host does not pollute the output
stream.
Quelle: Understanding Streams, Redirection, and Write-Host
in PowerShell
https://blogs.technet.microsoft.com/heyscriptingguy/2014/03/30/understanding-streams-redirection-and-write-host-in-powershell/
Hilfreicher was dann aber ein BlogPost von Jeffrey Snover, der PowerShell-Designer, der mit dem Satz „Using Write-Host is almost always wrong.“ startet und das auch erklärt.
- Write-Host considered harmful
http://www.jsnover.com/blog/2013/12/07/write-host-considered-harmful/ - Weekend Scripter: Welcome to the PowerShell Information Stream
https://devblogs.microsoft.com/scripting/weekend-scripter-welcome-to-the-powershell-information-stream/
Hinweis
Jeffrey Snover rät davon ab, mit "Write-Host" Debug-Ausgaben
zu schreiben, da sie "nur" auf der Konsole erscheinen und
daher nicht weiter verarbeitet werden können. Das
widerspricht dem Ziel Code wieder zu verwenden. Write-Host
ist aber durchaus in Ordnung für z.B. die Gestaltung
einfacher Oberflächen u.a.
Siehe auch
http://www.jsnover.com/blog/2013/12/07/write-host-considered-harmful/
Im ersten Moment war ich auch irritiert, so wie ich mein
„Write-host“ Liebgewonnen habe und natürlich möchte ich nun
nicht alles mit „Write-Output“ raus jagen. Vor allem stören
mich ja die „Bildschirmausgaben“ in der Pipeline für
nachfolgende Programme, die das alles gar nicht brauchen.
Also dann doch mehr mit einem „Write-Verbose/Write-Debug/Write-Error“
arbeiten ? Jemand hat sogar schon eine „Ersatzfunktion“
gebaut.
Function Write-Host ($message,$nonewline,$backgroundcolor,$foregroundcolor) { $Message | Out-Host }
So kann man seine Skript unangetastet lassen und die Ausgabe einfach auf ein "Out-Host" umleiten. Natürlich alles ohne die Farben etc.
Mittlerweile nutzt ich schon fast häufiger die Write-Output-Funktion denn so eine Umschreibe-Funktion, denn Write-Output nutzt die gleichen Parameter.
Write-Verbose
Nachdem nun allen klar ist, dass Write-Host" für Ausgaben auf den Bildschirm, und nur auf den Bildschirm, optimal ist, stellt sich natürlich die Frage wie ich eine Ausgabe so erstelle, dass ich sie sehen aber auch loggen kann. Dazu gibt es vier (und aber Powershell5 mit Write-Information noch eine fünfte) Optionen:
Write-Error Write-Warning Write-Verbose Write-Debug
Der große Unterschied zu Write-Host ist hier, dass die Ausgaben jeweils in eine eigene Pipe gehen, die aber per Default auch auf dem Bildschirm landet. Sie kann aber umgeleitet werden. Die Meldung selbst muss als ein String übergeben werden oder als Wert des Parameters "-Message". Insofern reicht einfaches "Suchen und Ersetzen" von Write-Host durch Write-Verbose meist nicht aus.
Ob Write-Verbose oder eine der der anderen Ausgaben wirklich was auf die Konsole schreiben hängt von den Preferences ab:
Preference | Default | Bemerkung |
---|---|---|
$VerbosePreference |
SilentlyContinue |
Das Skript wird weiter abgearbeitet und es wird keine Ausgabe auf die Konsole ausgegeben. Um dies zu ändern, muss die Preference auf "Continue" gestellt werden |
$WarningPreference |
Continue |
Die Warnung wird schon bei der Default-Einstellung ausgegeben |
$ErrorActionPreference |
Continue |
Die Warnung wird schon bei der Default-Einstellung ausgegeben |
$DebugPreference |
SilentlyContinue |
Das Skript wird weiter abgearbeitet und es wird keine Ausgabe auf die Konsole ausgegeben. Um dies zu ändern, muss die Preference auf "Continue" gestellt werden |
Wer also nun statt "Write-Host" das Logging über Write-Verbose erzeugt, wird per Default wieder nichts sehen, es sei denn er ändert die Preference z.B. mit.
$VerbosePreference="Continue"
Wenn Sie den Konzepten von PowerShell folgende, dann sollten Sie dies aber so nicht machen, sondern die Verbose-Ausgabe ebenfalls als Parameter für ihr Skript hinterlegen. Der erste Ansatz ist dann der folgende:
param ( [switch]$verbose ) if ($verbose) { $VerbosePreference="Continue" }
Noch besser ist es aber direkt über de CMDLetBinding zu gehen, welches aber immer einen Param-Block braucht:
[CMDLetBinding()] param () write-verbose "test"
Dann aber können Sie einfach den Parameter "-Verbose" addieren um die Ausgabe zu aktivieren
-
Capture Warning, Verbose, Debug
and Host Output via alternate
streams
https://connect.microsoft.com/PowerShell/feedback/details/297055/capture-warning-verbose-debug-and-host-output-via-alternate-streams -
Write-Information (Ab Powershell
5)
https://technet.microsoft.com/de-de/library/dn998020.aspx
Starting in Windows PowerShell 5.0, Write-Host is a wrapper for Write-Information. -
Tips on Implementing
Pipeline Support
https://learn-powershell.net/2013/05/07/tips-on-implementing-pipeline-support/
Sehr schöne beschreibung in Verbindung mit CMDLETBinding
Debug-Ausgaben und Start-Job
Ich hatte ja schon geschrieben, dass ich Write-Host auch gerne nutze, weil man damit mit "-nonewline" und den Farben auch die Ausgabe etwas gliedern kann. Aber ich muss Jeffrey Snover schon recht geben, dass solche Ausgaben ja auch sonst nur auf der Konsole erscheinen. Was passiert aber, wenn ein Modul eben gar keine Konsole hat, z.B.: wenn der Code als "Job" im Hintergrund läuft.
Hier durfte ich lernen, dass die Verbose und Debug-Ausgaben sehr wohl z.B. bei einem Job abgerufen werden können. Das funktioniert sogar während des Jobs. Der Aufruf des obigen Beispiels erfolgt mit:
start-job -FilePath .\psdebug.ps1
Die Ergebnisse des Jobs sind dann mit "Get-Job" abrufbar:
PS C:\> Get-Job | fl HasMoreData : True StatusMessage : Location : localhost Command : [CmdletBinding()] param() start-transcript -path c:\temp\transcript.txt $DebugPreference="Continue" write-host "Write-Host" write-verbose "Write-verbose" write-debug "Write-debug" write-error "Write-error" write-host "Write-host" out-host -inputobject "out-host" stop-transcript JobStateInfo : Completed Finished : System.Threading.ManualResetEvent InstanceId : 88ab8ac3-5b54-42a3-88a8-1835c4f2b69d Id : 2 Name : Job2 ChildJobs : {Job3} PSBeginTime : 04.08.2016 19:36:46 PSEndTime : 04.08.2016 19:36:58 PSJobTypeName : BackgroundJob Output : {} Error : {} Progress : {} Verbose : {} Debug : {} Warning : {} State : Completed
Allerdings startet mein Aufruf einen zweiten "Child-Job" und dessen Ausgabe sind interessant.
PS C:\temp> get-job 3 | fl StatusMessage : HasMoreData : True Location : localhost Runspace : System.Management.Automation.RemoteRunspace Command : [CmdletBinding()] param() start-transcript -path c:\temp\transcript.txt $DebugPreference="Continue" write-host "Write-Host" write-verbose "Write-verbose" write-debug "Write-debug" write-error "Write-error" write-host "Write-host" out-host -inputobject "out-host" stop-transcript JobStateInfo : Completed Finished : System.Threading.ManualResetEvent InstanceId : 0d2846fa-a500-497f-a0a8-5199784a8e4a Id : 3 Name : Job3 ChildJobs : {} PSBeginTime : 04.08.2016 19:36:57 PSEndTime : 04.08.2016 19:36:58 PSJobTypeName : Output : {} Error : {NotSupported,Microsoft.PowerShell.Commands.StartTranscriptComm and, Microsoft.PowerShell.Commands.WriteErrorException, NotSupp orted,Microsoft.PowerShell.Commands.StopTranscriptCommand} Progress : {} Verbose : {} Debug : {Write-debug} Warning : {} State : Completed
Hier ist dann auch bei "Debug" die Ausgabe zu sehen. Insofern gibt es schon einen Anreiz zukünftig Diagnoseausgaben statt mit Write-Host einfach mit "Write-Verbose" zu erstellen oder zumindest zusätzlich zu erstellen und Write-Host nur für die "schöne Ausgabe" auf den Bildschirm zu nutzen, die vielleicht nicht immer im Transcript erscheint.
- PS Debugging
- Write-Host Considered Harmful
http://www.jsnover.com/blog/2013/12/07/write-host-considered-harmful/
Microsoft ADSync
Microsoft selbst hat die Notwendigkeit solcher Routinen auch verstanden aber leider nicht in PowerShell eingebaut. So findet sich dann auch z.B. im ADSync-Installationsverzeichnis "C:\Program Files\Microsoft Azure Active Directory Connect\Tools" die Datei "ADConnectivityTool.psm1", in der folgende Funktion definiert wurde:
#Auxiliary Function that writes an output to a log file or the PowerShell window Function Write-Log { [CmdletBinding()] Param( [Parameter(Mandatory=$False, Position=1)] [string] $Message, [Parameter(Mandatory=$False, Position=2)] [string] $ForegroundColor = "Cyan" ) Switch ($ForegroundColor) { Green { $Severity = "SUCCESS" } Red { $Severity = "ERROR " } Default { $Severity = "INFO " } } If($LogFileLocation) { If($Message -ne "") { $Message = "[" + (Get-Date).ToString() + "] [$Severity] " + $Message Out-File -Append -FilePath $LogFileLocation -InputObject $Message } } Else { Write-Host $Message -ForegroundColor $ForegroundColor } }
Auch das ExchangeSetup-Skript hat eine Funktion und ich bin sicher, dass es noch ganz viele andere Varianten gibt. Logging und Protokollierung ist einfach ein notwendiger Baustein und es ist eigentlich schade, dass die PowerShell dort nichts mitbringt.
Der Ansatz ist nett aber funktioniert nur für Skripte, die dann auch konsequent auf Write-Host verzichten und die alternative Funktion nutzen.
Mein "Write-Log"
Mein Ansatz beruht darauf, dass ich einfach "Write-Host" umdefiniere
So einfach alle Skripte umzustellen finde ich natürlich nun nicht elegant. Ich würde schon gerne "Write-Host" weiter nutzen um Ausgaben elegant und formatiert auf die Konsole zu schreiben. Ich möchte aber auch, dass diese Daten im Transcript landen. Meist sind es ja Skripte, die klein anfangen und erst später größer und umfangreicher werden. Bislang stand ich dann vor dem Problem, in ein solches Skript ein vernünftiges Logging nachzurüsten. für VBScript hatte ich schon länger eine passender Klasse "Debugwriter" (Siehe VBSToolbox).
Ich habe dann erst mal eine kleine Testserie gemacht, indem ich eine Funktion "Write-Host" geschrieben habe, die ich z.B. vor dem Skripte einmal definiere:
Funktion | Beschreibung |
---|---|
Ersetzen von Write-Hostfunction Write-host ($message) { "test1: $($message)" } |
Ich habe erst einmal geprüft, was meine Skripte machen, wenn ich "Write-Host" neu definiere. Der aufruf von Write-Host wurde dann auf mein Skript umgeleitet |
function Write-host ($message) { write-host "Test2: $($message)" } |
Dann wolle ich natürlich wissen, was in der Funktion passiert. Wie erwartet kam es zu einer Rekursion und Endlosschleife. So geht es also nicht |
function Write-host ($message) { Microsoft.PowerShell.Utility\write-host "dd $($message)" } |
Nun habe ich das Modul explizit angegeben und damit die Schleife durchbrochen. |
Damit war der Weg vorgezeichnet. Ich musste mein eigene "Write-Host"-Funktion einfach so stricken, dass Sie meinen Anforderungen genügt und auch die ganzen Parameter mit nimmt, die ich dann aber auch ans native "Write-Host" wieder zur Ausgabe weitergeben kann. Einige "schlaue Erweiterungen würde ich mir da schon noch wünschen
Erweiterung | Beschreibung |
---|---|
Weiterverarbeitung per Syslog o.a. |
Meine Idee ist, nach einer weiteren Funktion zu suchen und wenn diese vorhanden ist, die Ausgabe auch dorthin zu senden. Quasi eine dynamische Erweiterung um Log4L, Syslog und andere Dinge einfach einzubinden. Denkbar ist natürlich auch eine generell globale Variable zur Steuerung des Skripts |
Severity |
Ich unterscheide eigentlich immer Info, Warn, Error, Debug1-n. Natürlich kann ich das mit "Write-Error" ausgeben aber ich nutze im Write-Host auch gerne ein Prefix wie "Err:", "Wrn:", "Inf:", gefolgt von einem Fehler |
Zeitstempel |
Wie oft schreibe ich in meine "Write-Host"-Debug-Ausgaben einen Zeitstempel mit Get-Date. Das kann ich mir sparen, wenn das die neue Write-Host-Funktion macht. |
Script-Name |
Wenn mehrere PowerShell Script und Module sich gegenseitig aufrufen, dann könnte die Write-Host Funktion auch diese Information addieren. |
Farbliche Ausgabe |
Ich kann mit "-foregroundcolor" und "-backgroundcolor" manuell Farben vorgeben. Zumindest bei Fehlern o.ä. könnte das Write-Host auch alleine machen |
Am Schluss kommt dann ein generische Ausgabeformat wie folgt raus:
2021-06-13 19:32:53Z, Scriptname, INF0, Dies ist eine Write-Host ausgabe 2021-06-13 19:32:53Z, Scriptname, WRN0, Dies ist eine Warnung, die Write-Host am 2021-06-13 19:32:53Z, Scriptname, ERR0, Dies ist dann eine Fehlermeldung
Als CSV-Datei kann man das einfacher in eine Datei schreiben und filtern. Alternative kann eine Ausgabe ins Eventlog o.ä. erfolgen
Also habe ich mir mal ein "durchschnittliches Script" angeschaut und habe gemerkt, dass ich sehr viele Write-Host-Zeilen nutze und über eine Einrückung quasi den Level oder die Wichtigkeit definieren. Da sich die Parameter als auch das Skriptverhalten bei Write-Host von Write-Warning und Write-Error (Skript könnte abbrechen) unterscheidet, nutze ich eigentlich nur noch Write host, aber addiere ein "ERR:" oder "WRN:" oder "INF": am Anfang. Das könnte also wie folgt aussehen:
# Muster eines PowerShell Script write-host "Script gestartet" write-host " weniger wichtig" write-host " noch weniger wichtig" write-host "WRN: Warnung, da passt was nicht" write-host "ERR: Das ist ein echtes Problem" write-host "Script beendet"
Ich wollte an meinen bestehenden Skripten eigentlich gar nicht ändern.
Mit dieser Vorarbeit habe ich die Grundlage für ein generisches Modul geschaffen, welches alle "Write-Host"-Aufrufe eines Skript einfängt und ggfls. weiter verarbeitet, in eine Datei schreibt oder z.B. per SYSLOG an eine andere Plattform meldet. Es sollte natürlich alle oder zumindest die wichtige Parameter von "Write-Host" verstehen und mitnehmen und vielleicht sogar einen "Mehrwert" daraus beziehen.
Das hier ist noch eine "Baustelle", denn das passende Modul ist noch nicht fertig.
Ich denke ich werde hier aber nicht weiter aktiv werden, sondern eine passende Erweiterung oder eben Write-Verbose nutzen.
Andere Libraries und Tools gibt es ja auch schon. Das Rad muss man ja nicht mehrfach erfinden
- Log4Posh - module implements log4net
with minimal impact to existing codebase
https://gallery.technet.microsoft.com/scriptcenter/Log4Posh-Powershell-module-4a04f85f - Eine eigene Logfunktion mit der
Powershell
http://www.liersch.info/blog/eine-eigene-logfunktion-mit-der-powershell/ - Write-Log PowerShell Logging Function
https://gallery.technet.microsoft.com/scriptcenter/Write-Log-PowerShell-999c32d0
Gerade die "Log4Posh-Funktion" ist für Enterprise-Logging sehr interessant.
Weitere Links
- PS Debugging
- PowerShell Streams
- VBSToolbox
-
Write-Host considered harmful
http://www.jsnover.com/blog/2013/12/07/write-host-considered-harmful/ - 3014136 PowerShell tra
-
nscript file
doesn't contain the correct information in
Windows Server 2012 R2
https://support.microsoft.com/en-us/kb/3014136 -
Use PowerShell to Write Verbose
Output
https://blogs.technet.microsoft.com/heyscriptingguy/2014/07/30/use-powershell-to-write-verbose-output/ -
Weekend Scripter: Welcome to the PowerShell Information Stream
https://devblogs.microsoft.com/scripting/weekend-scripter-welcome-to-the-powershell-information-stream/ -
2012 Scripting Games Commentary:
STOP USING WRITE-HOST!
http://powershell.com/cs/blogs/donjones/archive/2012/04/06/2012-scripting-games-commentary-stop-using-write-host.aspx -
Getting your Script Module
functions to inherit
"Preference" variables from the
caller
https://powershell.org/2014/01/13/getting-your-script-module-functions-to-inherit-preference-variables-from-the-caller/ -
How do I use verbose parameter
with powershell script (not
module)
https://powershell.org/forums/topic/how-do-i-use-verbose-parameter-with-powershell-script-not-module/ -
Clean Coding with Write-Verbose
http://dbadailystuff.com/2012/06/27/clean-coding-with-write-verbose -
Understanding Streams,
Redirection, and Write-Host in
PowerShell
https://blogs.technet.microsoft.com/heyscriptingguy/2014/03/30/understanding-streams-redirection-and-write-host-in-powershell/ -
Programmatically capture Verbose
Output in a PowerShell variable
http://blog.simonw.se/programmatically-capture-verbose-output-in-a-powershell-variable/ -
Weekend Scripter: Welcome to the
PowerShell Information Stream
https://blogs.technet.microsoft.com/heyscriptingguy/2015/07/04/weekend-scripter-welcome-to-the-powershell-information-stream/
Because Write-Host doesn't go to a stream, if you put Write-Host in a script, the end-User can't suppress it, can't save it, and can't redirect it. -
Capture Warning, Verbose, Debug
and Host Output via alternate
streams
https://connect.microsoft.com/PowerShell/feedback/details/297055/capture-warning-verbose-debug-and-host-output-via-alternate-streams -
Write-Information (Ab Powershell
5)
https://technet.microsoft.com/de-de/library/dn998020.aspx
Starting in Windows PowerShell 5.0, Write-Host is a wrapper for Write-Information. -
about_Redirection
https://technet.microsoft.com/de-de/library/hh847746.aspx -
Tee-Object
https://technet.microsoft.com/de-de/library/hh849937.aspx -
Logger.psm1 0.6
http://poshcode.org/1858 -
How to capture or redirect
Write-Host output in powershell
http://www.latkin.org/blog/2012/04/25/how-to-capture-or-redirect-write-host-output-in-powershell/ -
PowerShell Logging v2 – Easily
create log files
https://9to5it.com/powershell-logging-v2-easily-create-log-files/
https://www.powershellgallery.com/packages/PSLogging