简体   繁体   中英

PowerShell: how to capture (or suppress) write-host

I have a script call "a.ps1":

write-host "hello host"
"output object"

I want to call the script and obtain the output object, but I also want the standard output to be suppressed:

$result = .\a.ps1
# Don't want anything printed to the console here

Any hint?

There really is no easy way to do this.

A workaround is to override the default behavior of Write-Host by defining a function with the same name:

function global:Write-Host() {}

This is very flexible. And it works for my simplistic example above. However, for some unknown reason, it doesn't work for the real case where I wanted to apply (maybe because the called script is signed and for security reasons it doesn't allow caller to arbitrarily change the behavior).

Another way I tried was to modify the underlying Console's stdout by:

$stringWriter = New-Object System.IO.StringWriter
[System.Console]::SetOut($stringWriter)
[System.Console]::WriteLine("HI") # Outputs to $stringWriter
Write-Host("HI") # STILL OUTPUTS TO HOST :(

But as you can see, it still doesn't work.

The output object ie "output object " is output to standard output . So I don't think you want to suppress standard output. If you don't want anything printed to the console don't use Write-Host as that bypasses all streams (stdout, stderr, warning, verbose, debug) and displays directly to the host. There is currently no easy mechanism I'm aware of to redirect host output.

BTW why do you need to write "hello host" to the console if you don't want to see it displayed later?

OK, I did a little digging over it. You can use:

The Following Link

And do:

$result = .\1.ps1 | Select-WriteHost -Quiet
$result[1]

And then select the second object in the variable:

Another explanation

You can also change the script in a way that will not change Write-Host to Write-Output and just "remove" the Write-Host.

Done...

function Remove-WriteHost
{
   [CmdletBinding(DefaultParameterSetName = 'FromPipeline')]
   param(
     [Parameter(ValueFromPipeline = $true, ParameterSetName = 'FromPipeline')]
     [object] $InputObject,

     [Parameter(Mandatory = $true, ParameterSetName = 'FromScriptblock', Position = 0)]
     [ScriptBlock] $ScriptBlock
   )

   begin
   {
     function Cleanup
     {
       # Clear out our proxy version of Write-Host
       remove-item function:\write-host -ea 0
     }

     function ReplaceWriteHost([string] $Scope)
     {
         Invoke-Expression "function ${scope}:Write-Host { }"
     }

     Cleanup

     # If we are running at the end of a pipeline, 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 -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 -Scope 'local'
        & $scriptblock
      }
      else
      {
         # In a pipeline scenario, just pass input along
         $InputObject
      }
   }

   end
   {
      Cleanup
   }
}
$result = .\1.ps1 | Remove-WriteHost

Thanks to "latkin" for the original function :)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM