您能将Tee-Object重定向到标准输出吗?

4

我正在编写脚本,希望能够传递值并在屏幕上显示出来。

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

但是Tee-Object总是要求一个文件,而我只想在屏幕上看到它。


2
并不是Tee-Object所做的事情,尽管我可以理解为什么你会这样认为。你可能需要用ForEach-Object{Write-Host $_; $_}替换它,并通过循环运行所有内容。 - TheMadTechnician
“data_processor.exe”需要一次性获取所有输入行(实际上只有四行),每行都不是独立的。 - Sled
循环不会影响这个,但如果你想一次性获取所有四行,你应该在 Get-Content 上使用 -raw 开关。这将读取整个文件,然后可以将其传递到 ForEach-Object 循环中,其中所有四行将被写入主机并一次传递给 data_processor.exe。或者在 | data_processor.exe 之前将所有内容包含在括号中。 - TheMadTechnician
2个回答

8

为了补充zett42的有帮助的答案

如果你正在运行PowerShell(核心)7+,你可以将代表终端(控制台)的文件路径传递给(位置暗示的)-FilePath参数(在Windows PowerShell中,这会导致错误,不幸的是 - 请参见底部区域):

# 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
Get-Content data.txt | Tee-Object ($IsWindows ? '\\.\CON': '/dev/tty') | data_processor.exe

这个程序将所有数据通过传递,同时以通常的丰富格式打印到终端(控制台),一旦可用 - 与使用Tee-Object -Variable方法不同,该方法需要先在内存中收集所有输出(这在输出时间和内存消耗方面都是一个问题)。


Windows PowerShell 解决方案:自定义代理(包装)函数Tee-HostOut-Host包装起来,同时通过它传递输入;使用它来替代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()
  }
    
}
  • 实际上,Tee-Host 的行为类似于 PowerShell 7+ 中的 Tee-Object \\.\CON / Tee-Object /dev/tty,其中 Tee-Host 也可以工作。

  • 即使在 PowerShell 7+ 中,Tee-Host 也可能更可取,因为它无条件地使用了 彩色 输出,而 Tee-Object \\.\CON / Tee-Object /dev/tty 的着色行为取决于 $PSStyle.OutputRendering 的值。

使用包装 Out-Host 的可步进管道的代理函数确保了发送到主机时格式化输出的外观与直接发送输入时相同。


这个“Tee-Object CON”功能非常有用,感谢您分享。您能描述一下您是如何发现它被添加到PowerShell 7中的吗?我尝试搜索一些关于它何时被添加的信息,但没有太多的运气。 - ZenoArrow

6
你可以输出到一个变量而不是文件:
Get-Content data.txt | Tee-Object -Variable data | data_processor.exe
$data  # Output

这将内容传递给"data_processor.exe"并将其存储在变量$data中。只有当.exe完成后,数据才会显示。

使用ForEach-Object检查Get-Content的输出,在每行发送到.exe之前进行:

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

通过编写一个小的筛选函数,可以使这个模式更加简洁和可重复利用:

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

现在我们可以这样写:

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

备注:

Write-HostAndForward 适用于简单的输入,例如从 Get-Content 接收到的字符串,但对于复杂对象,它通常不会产生与我们在控制台中通常看到的相同输出。这是因为 Write-Host 只是使用 .ToString() 方法将输入转换为字符串,而跳过了 PowerShell 的丰富格式化系统。

你可能会尝试简单地将 Write-Host 替换为 Out-Host,但正如 mklement0 解释的那样,它会分别对输入对象进行格式化,这将为表格格式的输出每个对象产生一个标题。为了避免这种情况,mklement0 的答案展示了不同的方法来产生期望的格式化输出。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接