shawn在论坛中提问《PowerShell 自动调用exe程序并根据输出自动交互》,当时上班比较忙,只回答了思路。今天专门抽时间解决一下这个问题。其实我们可以把需求简化下:
- 打开另外一个控制台程序A.exe。
- 将A.exe的输出重定向到PowerShell控制台。
- 如果A.exe需要用户输入才能继续,用PowerShell代替用户输入,自动交互。
但是shawn并没有提供给我他所谓的A.exe,于是我照猫画虎自己简单写了一个Demo(MyConsole.exe),超简单的控制台程序:
static void Main(string[] args) { Console.WriteLine("请输入A:"); string a = Console.ReadLine(); Console.WriteLine("请输入B:"); string b = Console.ReadLine(); Console.WriteLine("AB={0}{1}",a,b); Console.ReadLine(); }
我们假定MyConsole.exe要接受的参数是固定的,那就杀鸡不用牛刀了,直接这样即可:
PS> '银行','是弱势群体' | .\MyConsole.exe 请输入A: 请输入B: AB=银行是弱势群体
从shawn的需求来看,他不确定A.exe输出什么,但是他知道A.exe可能会输出什么,并给可能的输出通过输入给予交互响应,显然上面的牛刀小试还不够。
再次回到本文的主题:“PowerShell重定向其它进程的输入和输出”
再次赘述下,.NET 是PowerShell的灵魂和土壤,有的时候当你有.NET编程经验,但是对PowerShell语言不是很熟练时,不妨先试着用.NET实现需求,最后再翻译成PowerShell脚本语言。
我就是这么过来的,先写了另一个控制台程序:PlayMyConsole.exe,直接看code吧:
class Program { static void Main(string[] args) { ProcessStartInfo psi = new ProcessStartInfo(); psi.FileName = @"E:\MyConsole.exe"; var process = new Process(); process.StartInfo = psi; process.StartInfo.RedirectStandardOutput = true; process.StartInfo.UseShellExecute = false; process.StartInfo.RedirectStandardInput = true; process.OutputDataReceived += Proc_OutputDataReceived; process.Start(); process.BeginOutputReadLine(); process.WaitForExit(); } private static void Proc_OutputDataReceived(object sender, DataReceivedEventArgs e) { Process pro = sender as Process; Console.WriteLine(e.Data); if (e.Data == "请输入A:") { Console.WriteLine("自动输入:我喜欢"); pro.StandardInput.WriteLine("我喜欢"); } else if (e.Data == "请输入B:") { Console.WriteLine("自动输入:PSTips.NET"); pro.StandardInput.WriteLine("PSTips.NET"); } } }
C#控制台上的输出为:
最后在翻译的过程中,难点是如何通过PowerShell给Process对象的OutputDataReceived事件添加订阅,已经测试通过,请直接看脚本,注释很详细奥,亲!
# 控制台程序路径 $MyConsole = 'E:\MyConsole.exe' # 设置进程启动信息 $psi= New-Object System.Diagnostics.ProcessStartInfo $psi.FileName = $MyConsole # 设置进程自动重定向输入和输出 $psi.RedirectStandardInput = $true $psi.RedirectStandardOutput = $true $psi.CreateNoWindow = $true $psi.UseShellExecute = $false $process = New-Object System.Diagnostics.Process $process.StartInfo = $psi # 给System.Diagnostics.Process添加OutputDataReceived事件的订阅 $sub = Register-ObjectEvent -InputObject $process -EventName OutputDataReceived -action { # 打印源进程的输出信息 Write-Host $Event.SourceEventArgs.Data # 注意: $Event输入自动化变量: # 包含了 Register-ObjectEvent 命令的Action参数中的上下文, # 尤其是Sender和Args # .Sender默认是Object对象,需要转换成Process对象 $p = [System.Diagnostics.Process]$Event.Sender # 接收源进程的输出,并根据输出写入相应的输入 if ($Event.SourceEventArgs.Data -eq "请输入A:") { Write-Host "自动输入:我喜欢" $p.StandardInput.WriteLine("我喜欢"); } elseif ($Event.SourceEventArgs.Data -eq "请输入B:") { Write-Host "自动输入:PSTips.NET" $p.StandardInput.WriteLine("PSTips.NET"); } } $process.Start() $process.BeginOutputReadLine()
PowerShell控制台上的输出为:
本文链接: https://www.pstips.net/redirect-process-output-and-inputs.html
请尊重原作者和编辑的辛勤劳动,欢迎转载,并注明出处!
请尊重原作者和编辑的辛勤劳动,欢迎转载,并注明出处!
终于有时间了,来总结一下这几天折腾这个问题的感想吧,顺便感谢一下管理员大神,越来越觉得power_shell是一个很有意思的语言了。
(1),对于我在这篇文章(http://www.pstips.net/question/4418.html)提出的问题,管理员在上面给出了一个简直是完美的答案,太厉害了,也非常感谢管理员的努力。
(2),对于本文的第一个牛刀小试程序,也就是“MyConsole.exe要接受的参数是固定的”这种情况,管理员给出的方法虽然简便,但是有一个小缺点。如果程序是经过多层调用,才执行到MyConsole.exe, ‘银行’,’是弱势群体’ | .\MyConsole.exe 这种方法可能会抓不到MyConsole.exe的输出信息。也就是程序虽然自动运行,但是在我们已经打开的控制台上是没有任何输出的。我觉的要是碰到这种比较极端的情况,可以手动去抓程序的输出,其实也很简单:
function CheckResult()
{
begin{ }
process
{
#$_是中间的输出信息,把抓到的输出信息打印到当前控制台。 process{}这个过程会循环执行,直到MyConsole.exe结束。
write-host $_
}
end{}
}
‘银行’,’是弱势群体’ | .\MyConsole.exe | CheckResult
这样可以强行把MyConsole.exe的信息输出到当前控制台。
以上算是这几天学习的一点小心得,班门弄斧啦,哈哈~~
找输入重定向找好久了,谢谢。