4

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.

3
  • 2
    Not really what Tee-Object does, though I can see why you'd think it should. You probably have to replace it with ForEach-Object{Write-Host $_; $_} instead and run everything through a loop. Commented Feb 8, 2022 at 19:09
  • data_processor.exe needs all the lines of input (only four actually) at once, each line isn't independent. Commented Feb 8, 2022 at 19:20
  • The loop won't affect that, but if you want all four lines at once you should be using the -raw switch on Get-Content. That will read the entire file, then it can be passed to the ForEach-Object loop where all four lines will be written to the host and passed at once to data_processor.exe. Or wrap everything before | data_processor.exe in parenthesis. Commented Feb 8, 2022 at 20:28

2 Answers 2

9

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 # Platform-agnostic # See coming future improvement below (-Host) Get-Content data.txt | Tee-Object ($IsWindows ? '\\.\CON': '/dev/tty') | data_processor.exe 

Note:

  • GitHub issue #19827 is a green-lit, but as yet (as of PowerShell (Core) 7.3.x) unimplemented improvement that will allow simplifying
    ($IsWindows ? '\\.\CON': '/dev/tty') to -Host.

This passes all data through while also printing it to the terminal (console), richly formatted, as usual, as it becomes available - unlike with the Tee-Object -Variable approach, which requires collecting all output in memory first (which is a concern both in terms of output timing and memory consumption).


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.

Sign up to request clarification or add additional context in comments.

Comments

8

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.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.