Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Redirect Write-Host statements to a file

Tags:

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?

like image 982
ChiliYago Avatar asked Apr 07 '11 23:04

ChiliYago


2 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 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.
like image 184
Emiliano Poggi Avatar answered Oct 03 '22 05:10

Emiliano Poggi


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    } } 
like image 31
latkin Avatar answered Oct 03 '22 05:10

latkin