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?
-
1I just found Start-Transcript which outputs everything from your window to a file but does not seem to work yet when I add it to ps1 file. – ChiliYago Apr 07 '11 at 23:34
13 Answers
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
- redirection operators like
>, >>, 2>, 2>, 2>&1
Write-Log
- Tee-Object
Start-Transcript
.

- 1
- 1

- 24,390
- 8
- 55
- 67
-
2I don't think this is an answer. In Powershell 3.0, if I try `write-host "test" > c:\temp\out.txt` I don't get anything in the file. the output is written to the console – ekkis Oct 02 '13 at 02:00
-
1@ekkis redirection operator is unuseful when applied to cmdlets without output, obviously – Emiliano Poggi Dec 16 '13 at 13:20
-
2yes but the answer states that if I don't want to replace the write-host statements that I should "try to append redirection" - which is what I tried in my answer, which fails – ekkis Dec 17 '13 at 18:41
-
1I see, I wrote "try" actually. Thank you for your feedback, I corrected the answer with better indications hopefully. – Emiliano Poggi Dec 18 '13 at 13:59
-
1[Write-Log in Internet Archive](https://web.archive.org/web/20170123204619/https://poshcode.org/2575) – eel ghEEz Aug 12 '22 at 17:05
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
}
}

- 30,738
- 21
- 105
- 131

- 16,402
- 1
- 47
- 62
You can run your script in a secondary PowerShell shell and capture the output like this:
powershell -File 'Your-Script.ps1' > output.log
That worked for me.

- 30,738
- 21
- 105
- 131

- 129
- 1
- 3
-
1Works great! Best solution I've found that doesn't require any changes to code or proxy functions. KISS – John Homer Oct 04 '17 at 19:19
-
1This worked for `Write-Output` but did not work for `Write-Host`. [Emiliano Poggi](https://stackoverflow.com/a/5752877/)'s answer seems more complete. – Trisped Feb 09 '23 at 22:18
Using redirection will cause Write-Host to hang. This is because Write-Host deals with various formatting issues that are specific to the current terminal being used. If you just want your script to have flexibility to output as normal (default to shell, with capability for >
, 2>
, etc.), use Write-Output.
Otherwise, if you really want to capture the peculiarities of the current terminal, Start-Transcript is a good place to start. Otherwise you'll have to hand-test or write some complicated test suites.

- 30,738
- 21
- 105
- 131

- 13,397
- 7
- 35
- 44
-
1I would nominate this answer as the correct answer since it basically states you cannot redirect write-host and instead suggests what to use in its place – ekkis Oct 02 '13 at 02:02
-
I think this should be the accepted answer. I'm not sure why it isn't?? ;) – AJ. Sep 16 '21 at 15:07
Try adding a asterisk *
before the angle bracket >
to redirect all streams:
powershell -File Your-Script.ps1
*
> output.log
When stream redirection is requested, if no specific stream is indicated then by default only the Success Stream
(1>
) is redirected. Write-Host
is an alias for Write-Information
which writes to the Information Stream
(6>
). To redirect all streams use *>
.
Powershell-7.1 supports redirection of multiple output streams:
- Success Stream (#1): PowerShell 2.0
Write-Output
- Error Stream (#2): PowerShell 2.0
Write-Error
- Warning Stream (#3): PowerShell 3.0
Write-Warning
- Verbose Stream (#4): PowerShell 3.0
Write-Verbose
- Debug Stream (#5): PowerShell 3.0
Write-Debug
- Information Stream (#6): PowerShell 5.0
Write-Information
- All Streams (*): PowerShell 3.0

- 41
- 1
This worked for me in my first PowerShell script that I wrote few days back:
function logMsg($msg)
{
Write-Output $msg
Write-Host $msg
}
Usage in a script:
logMsg("My error message")
logMsg("My info message")
PowerShell script execution call:
ps> .\myFirstScript.ps1 >> testOutputFile.txt
It's not exactly answer to this question, but it might help someone trying to achieve both logging to the console and output to some log file, doing what I reached here :)

- 30,738
- 21
- 105
- 131

- 1,387
- 5
- 16
- 30
-
5Be careful with this approach. Anything you write with write-output becomes part of the return value of the function, which can be surprising, especially if there is an explicit return statement later in the function. – skiggety Apr 22 '16 at 15:44
If you have just a few Write-Host statements, you can use the "6>>"
redirector operator to a file:
Write-Host "Your message." 6>> file_path_or_file_name
This is the "Example 5: Suppress output from Write-Host" provided by Microsoft, modified accordingly to about_Operators.

- 65
- 4
-
"6" means: *"Information Stream. Introduced in PowerShell 5.0."* – Peter Mortensen Sep 29 '19 at 16:39
Define a function called Write-Host. Have it write to a file. You may have some trouble if some invocations use a weird set of arguments. Also, this will only work for invocations that are not Snapin qualified.

- 737
- 3
- 7
I just added Start-Transcript at the top of the script and Stop-Transcript at the bottom.
The output file was intended to be named <folder where script resides>-<datestamp>.rtf
, but for some reason the trace file was being put where I did not expect it — the desktop!

- 30,738
- 21
- 105
- 131

- 11,341
- 23
- 79
- 126
I have found the best way to handle this is to have a logging function that will detect if there is a host UI and act accordingly. When the script is executed in interactive mode it will show the details in the host UI, but when it is run via WinRM or in a non-interactive mode it will fall back on the Write-Output so that you can capture it using the >
or *>
redirection operators
function Log-Info ($msg, $color = "Blue") {
if($host.UI.RawUI.ForegroundColor -ne $null) {
Write-Host "`n[$([datetime]::Now.ToLongTimeString())] $msg" -ForegroundColor $color -BackgroundColor "Gray"
} else {
Write-Output "`r`n[$([datetime]::Now.ToLongTimeString())] $msg"
}
}
In cases where you want to capture the full output with the Write-Host coloring, you can use the Get-ConsoleAsHtml.ps1 script to export the host's scrolling buffer to an HTML or RTF file.

- 30,738
- 21
- 105
- 131

- 14,929
- 12
- 80
- 104
-
2Write-Output can be dangerous if someone uses the function Log-Info inside a pipeline or a function. You'll have then suddenly the console output in the pipeline or as function return value. – Mehrdad Mirreza Nov 08 '16 at 16:40
Use Write-Output instead of Write-Host, and redirect it to a file like this:
Deploy.ps1 > mylog.log or Write-Output "Hello World!" > mylog.log

- 30,738
- 21
- 105
- 131
You should not use Write-Host if you wish to have the messages in a file. It is for writing to the host only.
Instead you should use a logging module, or Set/Add-Content.

- 14,195
- 22
- 56
- 52
Try using Write-Output instead of Write-Host.
The output goes down the pipeline, but if this is the end of the pipe, it goes to the console.
> Write-Output "test"
test
> Write-Output "test" > foo.txt
> Get-Content foo.txt
test

- 30,738
- 21
- 105
- 131

- 18,205
- 2
- 28
- 58