C#代码启动进程,重定向stdin / stdout,后续ReadLine()从stdout失败

时间:2018-03-30 22:45:27

标签: c# python io-redirection

我有C#代码正在启动一个恰好是运行脚本的python解释器的进程。目标是将该解释器的stdin和stdout重定向为与脚本进行进程间通信的方法。代码如下:

        String py_path = @"C:\Anaconda3\python.exe";
        String script_path = @"<python script>";
        String working_dir = @"<directory of python script>";
        // Create new process start info 
        ProcessStartInfo myProcessStartInfo = new ProcessStartInfo(py_path); 
        // redirect standard in and out 
        myProcessStartInfo.UseShellExecute = false; 
        myProcessStartInfo.RedirectStandardOutput = true;
        myProcessStartInfo.RedirectStandardInput = true; 
        myProcessStartInfo.Arguments = script_path;
        myProcessStartInfo.WorkingDirectory = working_dir;
        Process myProcess = new Process(); 

        // assign start information to the process 
        myProcess.StartInfo = myProcessStartInfo;

        // start process
        myProcess.Start();

        //read one line from stdout
        StreamReader myStreamReader = myProcess.StandardOutput; 
        String return_value_1 = myStreamReader.ReadLine();

        if(return_value1 == null)
            Print("Null return value");
        else
            Print(return_value1);

此代码失败(return_value1null),即使python脚本执行的第一件事是在stdout上打印一行。

但是,如果我注释掉标准输入的重定向(在本例中未触及),如下所示:

        String py_path = @"C:\Anaconda3\python.exe";
        String script_path = @"<python script>";
        String working_dir = @"<directory of python script>";
        // Create new process start info 
        ProcessStartInfo myProcessStartInfo = new ProcessStartInfo(py_path); 
        // redirect standard in and out 
        myProcessStartInfo.UseShellExecute = false; 
        myProcessStartInfo.RedirectStandardOutput = true;
        //myProcessStartInfo.RedirectStandardInput = true; 
        myProcessStartInfo.Arguments = script_path;
        myProcessStartInfo.WorkingDirectory = working_dir;
        Process myProcess = new Process(); 

        // assign start information to the process 
        myProcess.StartInfo = myProcessStartInfo;

        // start process
        myProcess.Start();

        //read one line from stdout
        StreamReader myStreamReader = myProcess.StandardOutput; 
        String return_value_1 = myStreamReader.ReadLine();

        if(return_value1 == null)
            Print("Null return value");
        else
            Print(return_value1);

然后代码打印python脚本写入stdout的第一行,正如我所料。

显然,以某种方式重定向stdin会导致重定向的stdout的ReadLine()返回null,但我不理解这种交互。

此处描述的死锁问题有可能存在: https://msdn.microsoft.com/en-us/library/system.diagnostics.processstartinfo.redirectstandardoutput.aspx 以某种方式参与其中,但我不会立即看到情况如何。

1 个答案:

答案 0 :(得分:0)

首次使用例子 myProcess.StandardInput.Close();之后的myProcess.Start()。您应该在获得输出之前发送所有输入。

虽然您想要StandardError的输出和错误

string py_path = @"C:\Anaconda3\python.exe";
string script_path = @"<python script>";
string working_dir = @"<directory of python script>";
var processStartInfo = new ProcessStartInfo(py_path)
{
    Arguments = script_path,
    WorkingDirectory = working_dir,
    UseShellExecute = false,
    CreateNoWindow = true,
    RedirectStandardInput = true,
    RedirectStandardOutput = true,
    RedirectStandardError = true
};
var process = new Process { StartInfo = processStartInfo };
process.Start();
process.StandardInput.Close(); 

var stdOut = Task.Factory.StartNew(() => process.StandardOutput.ReadToEnd());
var stdErr = Task.Factory.StartNew(() => process.StandardError.ReadToEnd());
process.WaitForExit();
var output = stdOut.Result;
var error = stdErr.Result

我使用了Task,因为如果stdout或stderr有大量数据,缓冲区可能已满,应用程序将挂起

我还添加了一种异步退出/错误的方法,你可以写入输入

string py_path = @"C:\Anaconda3\python.exe";
string script_path = @"<python script>";
string working_dir = @"<directory of python script>";
var processStartInfo = new ProcessStartInfo(py_path)
{
    Arguments = script_path,
    WorkingDirectory = working_dir,
    UseShellExecute = false,
    CreateNoWindow = true,
    RedirectStandardInput = true,
    RedirectStandardOutput = true,
    RedirectStandardError = true,
};
var process = new Process { StartInfo = processStartInfo };

var outputBuffer = new StringBuilder();
var errorBuffer = new StringBuilder();
process.OutputDataReceived += (s, ea) => outputBuffer.AppendLine(ea.Data);
process.ErrorDataReceived += (s, ea) => errorBuffer.AppendLine(ea.Data);
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();

process.StandardInput.WriteLine();

// from now on can write what ever to StandardInput and data will be into output
// and error buffer. Sometimes might want synchronization between read and write 
// also might want to empty buffers