I have a PowerShell script that I am debugging and would like to redirect all Write-Host statements to a file. Is there an easy way to do that?
Until PowerShell 4.0,
Write-Host
sends the objects to the host. It does not return any objects.Beginning with PowerShell 5.0 and newer,
Write-Host
is a wrapper forWrite-Information
, which allows to output to the information stream and redirect it with6>> file_name
.
http://technet.microsoft.com/en-us/library/hh849877.aspx
However, if you have a lot of Write-Host
statements, replace them all with Write-Log
, which lets you decide whether output to console, file or event log, or all three.
Check also:
Add-Content
>, >>, 2>, 2>, 2>&1
Write-Log
Start-Transcript
.You can create a proxy function for Write-Host which sends objects to the standard output stream instead of merely printing them. I wrote the below cmdlet for just this purpose. It will create a proxy on the fly which lasts only for the duration of the current pipeline.
A full writeup is on my blog here, but I've included the code below. Use the -Quiet
switch to suppress the console write.
Usage:
PS> .\SomeScriptWithWriteHost.ps1 | Select-WriteHost | out-file .\data.log # Pipeline usage PS> Select-WriteHost { .\SomeScriptWithWriteHost.ps1 } | out-file .\data.log # Scriptblock usage (safer) function Select-WriteHost { [CmdletBinding(DefaultParameterSetName = 'FromPipeline')] param( [Parameter(ValueFromPipeline = $true, ParameterSetName = 'FromPipeline')] [object] $InputObject, [Parameter(Mandatory = $true, ParameterSetName = 'FromScriptblock', Position = 0)] [ScriptBlock] $ScriptBlock, [switch] $Quiet ) begin { function Cleanup { # Clear out our proxy version of write-host remove-item function:\write-host -ea 0 } function ReplaceWriteHost([switch] $Quiet, [string] $Scope) { # Create a proxy for write-host $metaData = New-Object System.Management.Automation.CommandMetaData (Get-Command 'Microsoft.PowerShell.Utility\Write-Host') $proxy = [System.Management.Automation.ProxyCommand]::create($metaData) # Change its behavior $content = if($quiet) { # In quiet mode, whack the entire function body, # simply pass input directly to the pipeline $proxy -replace '(?s)\bbegin\b.+', '$Object' } else { # In noisy mode, pass input to the pipeline, but allow # real Write-Host to process as well $proxy -replace '(\$steppablePipeline\.Process)', '$Object; $1' } # Load our version into the specified scope Invoke-Expression "function ${scope}:Write-Host { $content }" } Cleanup # If we are running at the end of a pipeline, we need # to immediately inject our version into global # scope, so that everybody else in the pipeline # uses it. This works great, but it is dangerous # if we don't clean up properly. if($pscmdlet.ParameterSetName -eq 'FromPipeline') { ReplaceWriteHost -Quiet:$quiet -Scope 'global' } } process { # If a scriptblock was passed to us, then we can declare # our version as local scope and let the runtime take # it out of scope for us. It is much safer, but it # won't work in the pipeline scenario. # # The scriptblock will inherit our version automatically # as it's in a child scope. if($pscmdlet.ParameterSetName -eq 'FromScriptBlock') { . ReplaceWriteHost -Quiet:$quiet -Scope 'local' & $scriptblock } else { # In a pipeline scenario, just pass input along $InputObject } } end { Cleanup } }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With