在powershell控制台上重定向输入输出

PowerShell重定向其它进程的输入和输出 2


shawn在论坛中提问《PowerShell 自动调用exe程序并根据输出自动交互》,当时上班比较忙,只回答了思路。今天专门抽时间解决一下这个问题。其实我们可以把需求简化下:

  1. 打开另外一个控制台程序A.exe。
  2. 将A.exe的输出重定向到PowerShell控制台。
  3. 如果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#控制台上的输出为:

C#重定向控制台输入和输出

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控制台上的输出为:

在powershell控制台上重定向输入输出

在powershell控制台上重定向输入输出

本文链接: https://www.pstips.net/redirect-process-output-and-inputs.html
请尊重原作者和编辑的辛勤劳动,欢迎转载,并注明出处!

关于 Mooser Lee

我是一个Powershell的爱好者,创建了PowerShell中文博客,热衷于Powershell技术的搜集和分享。本站部分内容来源于互联网,不足之处敬请谅解,并欢迎您批评指正。

回复 shawn 取消回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

2 条评论 “PowerShell重定向其它进程的输入和输出

  • shawn

    终于有时间了,来总结一下这几天折腾这个问题的感想吧,顺便感谢一下管理员大神,越来越觉得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的信息输出到当前控制台。

    以上算是这几天学习的一点小心得,班门弄斧啦,哈哈~~