将Process StandardOutput连接到另一个Process StandardInput

本文关键字:Process 另一个 StandardInput 连接 StandardOutput | 更新日期: 2023-09-27 18:22:38

我正在尝试在几个Process对象之间创建一个管道。

我可以运行单个Process并捕获其StandardOutput,但当我尝试连接多个Process对象时,第二个对象似乎不会从第一个对象的StandardInput接收任何数据。

在这段代码中,我使用的是cat,它要么将参数的内容打印到stdout,要么只是将stdin复制到stdout。

使用一个过程效果良好:

// Works
private async void launchButtonClicked(object sender, EventArgs e)
{
    var outputBuffer = new MemoryStream();
    var tasks = new List<Task>();
    Process process1;
    {
        process1 = new Process();
        process1.StartInfo.FileName = "cat.exe";
        process1.StartInfo.Arguments = "K:''temp''streams.txt";
        process1.StartInfo.UseShellExecute = false;
        process1.StartInfo.RedirectStandardOutput = true;
        process1.StartInfo.RedirectStandardInput = false;
        process1.StartInfo.CreateNoWindow = true;
        process1.EnableRaisingEvents = true;
        process1.Start();
    }
    tasks.Add(process1.StandardOutput.BaseStream.CopyToAsync(outputBuffer));
    await Task.WhenAll(tasks);
    // OK: This prints the contents of the file
    Console.WriteLine("Final output: {0}", UTF8Encoding.UTF8.GetString(outputBuffer.GetBuffer()));
}

但是,当我添加第二个进程并尝试将Process1的StandardOutput复制到Process2的StandardInput时,我从未从Process2获得任何输出,等待也从未完成:

// Doesn't work
private async void launchButtonClicked(object sender, EventArgs e)
{
    var outputBuffer = new MemoryStream();
    var tasks = new List<Task>();
    Process process1, process2;
    {
        process1 = new Process();
        process1.StartInfo.FileName = "cat.exe";
        process1.StartInfo.Arguments = "K:''temp''streams.txt";
        process1.StartInfo.UseShellExecute = false;
        process1.StartInfo.RedirectStandardOutput = true;
        process1.StartInfo.RedirectStandardInput = false;
        process1.StartInfo.CreateNoWindow = true;
        process1.Start();
    }
    {
        process2 = new Process();
        process2.StartInfo.FileName = "cat.exe";
        process2.StartInfo.Arguments = "";
        process2.StartInfo.UseShellExecute = false;
        process2.StartInfo.RedirectStandardOutput = true;
        process2.StartInfo.RedirectStandardInput = true;
        process2.StartInfo.CreateNoWindow = true;
        process2.Start();
    }
    tasks.Add(process1.StandardOutput.BaseStream.CopyToAsync(process2.StandardInput.BaseStream));
    tasks.Add(process2.StandardOutput.BaseStream.CopyToAsync(outputBuffer));
    await Task.WhenAll(tasks); // Never returns!
    Console.WriteLine("Final output: {0}", UTF8Encoding.UTF8.GetString(outputBuffer.GetBuffer()));
}

从命令行执行这样的操作效果很好:

C:'>cat.exe K:'temp'streams.txt | cat.exe
... contents of file ...
C:'>

我尝试将Exited事件处理程序添加到这些Process对象中。Process1正常退出,但Process2从不退出。

我也尝试了一些其他命令(比如sed),但Process2似乎仍然什么都没做。

我使用流的BaseStream属性,因为我最终将使用二进制数据。(https://stackoverflow.com/a/4535927/339378)这也意味着我不能使用OutputDataReceived,它返回一个字符串(无论如何都会更复杂)。

谢谢!

将Process StandardOutput连接到另一个Process StandardInput

显然,CopyToAsync不会在完成时关闭输出流。这对我的MemoryStream来说并不重要,但显然我需要关闭流程的StandardInput,否则它将永远无法完成。

我做了这个方法:

private static async Task CopyThenClose(Stream from, Stream to)
{
    await from.CopyToAsync(to);
    to.Close();
}

并替换了对CopyToAsync的调用;例如:

tasks.Add(CopyThenClose(process1.StandardOutput.BaseStream, process2.StandardInput.BaseStream));