首页 > 解决方案 > 我可以在 Powershell 中打开无缓冲程序输出吗?

问题描述

我正在尝试将 Putty 的 plink.exe 用作 Powershell 脚本的一部分,但在输出时遇到了麻烦。

一些命令调用交互式响应(例如:输入密码)。具体来说,我正在针对 Isilon 进行测试。

示例代码:

$command = '&"C:\Program Files\Putty\plink.exe" root@10.0.0.141 -pw "password" -t -batch "isi auth users create testuser --set-password"'  
iex $command

预期结果:

如果我尝试 tee 输出,使用iex $command | tee-object -variable result,甚至只是重定向iex $command *>test.log,提示文本直到我响应它之后才会显示。虽然在技术上仍然可以正常工作,但如果您不确切知道会出现什么提示,那么它是无用的。

我试过使用 Start-Transcript,但这根本没有捕获输出。我也尝试过使用 plink 的 -sshlog 参数,但是记录太多了,格式不可读。

有没有办法让标准输出在控制台中不受缓冲,并将其存储在变量中?

要回答一些潜在的问题:

-这是在不允许模块的环境中运行的,因此不能使用 Posh-SSH。

- 可用的 Powershell 版本不够新,无法使用内置的 openssh 功能。

标签: powershellstdoutputtyplink

解决方案


这都是关于重定向流的。

当您使用重定向时,所有输出都从流中重定向,并传递以写入文件。当你执行:

Write-Host "Some Text" *>out.txt

您看不到任何输出,并且全部重定向到该文件。

关键说明:重定向是逐行(简化)工作的,因为重定向是通过一次一行地写入文件来工作的。

同样,当您使用 时Tee-Object所有输出都从流重定向到管道。这将传递给 cmdlet Tee-ObjectTee-Object接受输入,然后将该输入写入您想要的变量/文件和屏幕。这发生收集和处理输入之后。

这意味着重定向和Tee-Object命令都是逐行工作的。重定向和命令都以这种方式工作是有意义Tee-Object的,因为在尝试编辑和维护打开的文件的同时,很难处理诸如删除字符、四处移动和动态编辑文本之类的事情。它只设计为单向的,一旦语句完成,输出。

在这种情况下,交互运行时,password:提示会写入屏幕,您可以做出响应。

重定向/Teeing 输出时,password:文本提示被重定向并缓冲,等待您的响应。这是有道理的,因为该语句尚未完成。您不想发送半个可能更改的语句,或半个对象到管道中。只有您完成语句(例如输入密码 + 回车)之后,整个语句才会沿流/管道传递。发送整个语句后,它会被重定向/输出 Tee'd 并可以显示。

@Bill_Stewart 是正确的,因为您应该选择交互式提示或全自动解决方案。


编辑:从评论中添加更多信息。

如果我们使用Tee-Object它依赖于管道。管道只能将完整的对象传递到管道中(例如,完整的字符串,包括新线)。管道必须与其他命令交互,例如ForEach-Objector Select-Object,并且它们无法处理将不完整的数据传递给它们。这就是PowerShell 控制台的工作方式,您无法更改它。

同样,重定向是逐行进行的。背后的原因,我稍后会解释为什么。

所以,如果你想逐个字符地与它交互,那么你正在处理。而如果你想直接处理流,它会复杂100倍,因为你不能使用PowerShell控制台的便利,你必须直接手动运行进程并自己处理所有输入和输出。

首先,您必须手动启动该过程。为此,我们使用System.Diagnostics.Process类。伪代码看起来像这样:

$p = [System.Diagnostics.Process]::New()
$p.StartInfo.RedirectStandardOutput = $true
$p.StartInfo.RedirectStandardError = $true
$p.StartInfo.RedirectStandardInput = $true
$p.StartInfo.UseShellExecute = $false
#$p.StartInfo.CreateNoWindow = $true
$p.StartInfo.FileName = "plink.exe"
$p.StartInfo.Arguments = 'root@10.0.0.141 -pw "password" -t -batch "isi auth users create testuser --set-password"'
$p.EnableRaisingEvents = $true
....

我们实质上是创建进程,指定我们将重定向标准输出(StartInfo.RedirectStandardOutput = $true)以及标准输入到其他东西以供我们处理。我们如何知道何时读取数据?好吧,这个类有Process.OutputDataReceived Event。您绑定到此事件以读取附加数据。但:

OutputDataReceived 事件表明关联的 Process 已将一行以换行符结尾的行写入其重定向的 StandardOutput 流。

评论

因此,即使流程类也围绕流数据的换行符展开。这就是为什么甚至重定向*>都是逐行工作的。PowerShell、cmd等都使用Process类作为运行进程的基础。它们都绑定到相同的事件和方法来进行处理。因此,为什么一切都围绕换行符和语句完成。

(大口喘气)所以。您仍然想一次一个角色以交互方式处理事物吗?那么你不能使用事件的便利。您将不得不回退到使用 Stream Reader 并直接绑定到Process.StandardOutput Property。不幸的是,这就是我停下来的地方,并说要实现这一点超出了 SO 的范围,并且需要更多的研究才能完成。


推荐阅读