开发者

Redirect Write-Host statements to a file

I have a PowerShell script that I am debugging and would like to redirect all Write-Host state开发者_高级运维ments 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 for Write-Information, which allows to output to the information stream and redirect it with 6>> 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.


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


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.


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.


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


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 :)


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.


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.


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!


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.


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.


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


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
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜