Home > Software engineering >  Can you redirect Tee-Object to standard out?
Can you redirect Tee-Object to standard out?

Time:02-10

I am writing a script and I want to pass the values but also see them displayed

Get-Content data.txt | Tee-Object | data_processor.exe

But Tee-Object always requests a file and I just want to see it on the screen.

CodePudding user response:

You can output to a variable instead of a file:

Get-Content data.txt | Tee-Object -Variable data | data_processor.exe
$data  # Output

This passes content to "data_processor.exe" and stores it in variable $data. Data will be shown only when the .exe has finished.

Use ForEach-Object to examine output of Get-Content before each line is being send to the .exe:

Get-Content data.txt | ForEach-Object {
    Write-Host $_   # Output line to console
    $_              # Forward line to next command in chain 
} | data_processor.exe

This pattern could be made more succinct and reusable, by writing a small filter function:

Filter Write-HostAndForward {
    Write-Host $_   # Output line to console
    $_              # Forward line to next command in chain    
}

Now we can write:

Get-Content data.txt | Write-HostAndForward | data_processor.exe

Remarks:

While Write-HostAndForward works for simple input, like strings received from Get-Content, for complex objects it typically doesn't produce the same output as we normally see in the console. That is because Write-Host simply converts the input to string using the .ToString() method, which skips PowerShells rich formatting system.

You might be tempted to simply replace Write-Host by Out-Host, but as mklement0 explains, it would format the input objects individually, which will produce a header for each object for table-formatted output. To avoid that, mklement0's answer shows different ways to produce the expected formatted output.

CodePudding user response:

To complement zett42's helpful answer:

If you're running PowerShell (Core) 7 , you can pass the file path that represents the terminal (console) to the (positionally implied) -FilePath parameter (in Windows PowerShell, this causes an error, unfortunately - see bottom section):

# PowerShell 7  only

# Windows
Get-Content data.txt | Tee-Object CON | data_processor.exe

# Unix-like platforms (Linux, macOS)
Get-Content data.txt | Tee-Object /dev/tty | data_processor.exe

This passes all data through while also printing it to the terminal (console), richly formatted, as usual.


Windows PowerShell solution: Custom proxy (wrapper) function Tee-Host wraps Out-Host while also passing its input through; use it instead of Tee-Object:

function Tee-Host {  
  [CmdletBinding()]
  param(
    [Parameter(ValueFromPipeline)] $InputObject
  )

  begin
  {
    $scriptCmd = { Out-Host }
    $steppablePipeline = $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin)
    $steppablePipeline.Begin($PSCmdlet)
  }
  
  process
  {
    # Pass to Out-Host, and therefore to the host (terminal)
    $steppablePipeline.Process($InputObject)
    # Pass through (to the success stream)
    $InputObject 
  }
  
  end
  {
    $steppablePipeline.End()
  }
    
}
  • In effect, Tee-Host behaves like Tee-Object CON / Tee-Object /dev/tty in PowerShell 7 , where Tee-Host works too.

  • Even in PowerShell 7 Tee-Host may be preferable, because it uses colored output unconditionally, whereas the coloring behavior of Tee-Object CON / Tee-Object /dev/tty depends on the value of $PSStyle.OutputRendering

It is the use of a proxy function with a steppable pipeline wrapping Out-Host that ensures that the to-host formatted output looks the same as when the input is directly sent to the host.

  • Related