如何运行Python解释器并使用Java获取其输出?

是否可以使用Java从Python获取控制台输出? 以下是此类输出的示例:

Python 3.3.4 (v3.3.4:7ff62415e426, Feb 10 2014, 18:13:51) [MSC v.1600 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information. >>> 2+2 4 >>> 

现在,主要目标是通过使用Java调用Python解释器来获得上述输出。 这是我的尝试:

 //... //Irrelevant code omitted ProcessBuilder processBuilder = new ProcessBuilder("cmd"); processBuilder.redirectErrorStream(true); processBuilder.start(); processBuilder.command("python2"); Process pythonProcess = processBuilder.start(); OutputStream outputStream = pythonProcess.getOutputStream(); OutputStreamWriter osw = new OutputStreamWriter(outputStream); osw.write("2+2\r\nquit()\r\n"); osw.flush(); osw.close(); InputStream inputStream = pythonProcess.getInputStream(); BufferedReader bufferedReader = new BufferedReader( new InputStreamReader(inputStream)); String line; while( (line=bufferedReader.readLine())!=null) { System.out.println(line); } //... //Irrelevant code omitted 

我知道调用start方法会生成一个带有执行环境的新进程。 将python2写入进程的输出流会导致创建另一个进程。 这是问题开始的时候。 我无法找到一种方法将命令2+2发送到Python解释器(这是CMD的子进程)而不是其父进程。

总结一下:如何运行Python解释器,在其中执行一些命令,最后将结果打印到标准输出?

Python可执行文件可以告诉您正在以非交互方式运行该命令。 一旦它意识到它以非交互方式运行,它将不再尝试与您交互; 如果没有人,为什么还要打印到stdout或从stdin读取?

要看到这是真的,你会尝试运行例如“ls”或“ps”并看到它们在你的程序中工作,但然后运行例如“ftp”或“telnet”或“python”并看到它不起作用什么也没输出

在Linux的说法中,问题是我们运行进程的方式不会将TTY附加到它们。 解决方案是通过创建一个PTY来欺骗他们相信另一端有TTY。

欺骗应用程序认为它的标准输入是交互式的,而不是管道

上:

  • 我的Mac OS X 10.9.4笔记本电脑,CPython 2.7.5和Java 1.8.0_05和
  • 一个带有CPython 2.7.5和Java 1.7.0_55的Ubuntu 12.04.4 LTS服务器

以下作品,虽然非常丑陋:

 import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.InputStream; import java.io.InputStreamReader; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; class Foo { public static void main(String[] args) throws IOException, InterruptedException { // https://stackoverflow.com/questions/1401002/trick-an-application-into-thinking-its-stdin-is-interactive-not-a-pipe // // Using script or unbuffer is the important catch. Without this // step you cannot use stdin in Python interactively, even with // python -u. At least script comes with Linux/Mac OS X, but // unbuffer works fine too. ProcessBuilder pb; switch(System.getProperty("os.name")) { case "Mac OS X": pb = new ProcessBuilder( "/usr/bin/script", "-q", "/dev/null", "/usr/bin/python"); break; default: // Linux pb = new ProcessBuilder( "/usr/bin/script", "-qfc", "/usr/bin/python", "/dev/null"); } // This doesn't make a difference. // pb.redirectErrorStream(true); Process p = pb.start(); char[] readBuffer = new char[1000]; InputStreamReader isr = new InputStreamReader(p.getInputStream()); BufferedReader br = new BufferedReader(isr); int charCount; boolean written = false; while(true) { if (!br.ready() && !written) { // Ugly. Should be reading for '>>>' prompt then writing. Thread.sleep(1000); if (!written) { written = true; OutputStream os = p.getOutputStream(); OutputStreamWriter osw = new OutputStreamWriter(os); BufferedWriter bw = new BufferedWriter(osw); bw.write("2+2"); bw.newLine(); bw.write("quit()"); bw.newLine(); bw.flush(); bw.close(); } continue; } charCount = br.read(readBuffer); if (charCount > 0) System.out.print(new String(readBuffer, 0, charCount)); else break; } } } 

我不会这样做。 相反,我会使用线程来保持交互并避免阻塞读取并执行期望的操作,即在写出之前等待某些提示。 在上面的代码我盲目地睡觉然后希望最好。

但是我注意到你正在使用Windows,因为你已经运行了“cmd”。 我不知道如何在Windows上创建PTY,抱歉。 我想尽管你可以获得Windows的Expect; unbuffer是Expect中的实用程序:

http://expect.sourceforge.net/

或者试试cygwin,但我还没有测试过。

有关TTY和PTY的更多背景信息,请参阅:

如何创建一个用于读取输出和写入输入的伪tty

Asim Ihsan的答案有正确的根本原因 – python会知道它没有连接到交互式终端而且没有按照你想要的方式行事。 但是,您可以通过在启动python时传递-i标志来使python以交互模式运行。 这比Asim提出的要简单。 你应该直接启动python(不需要首先启动cmd的中介)。 你会用到类似的东西

 ProcessBuilder pb = new ProcessBuilder('python', '-i'); 

然后像原始问题一样或多或少地进行。 启动该过程,获取关联的流,以及读取和写入流。