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

时间:2014-07-30 15:59:24

标签: java python

是否可以使用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解释器,在其中执行一些命令,最后将结果打印到标准输出?

2 个答案:

答案 0 :(得分:3)

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

要确定这是真的,您可以尝试运行,例如“ls”或“ps”和 看到他们在你的程序中工作,但然后运行,例如“ftp”或“telnet” 或“python”,看看它不起作用,什么都不输出。

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

Trick an application into thinking its stdin is interactive, not a pipe

在:

  • 我的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,抱歉。我想虽然 你可以得到Expect for Windows; unbuffer是一个实用工具 期望:

http://expect.sourceforge.net/

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

有关TTY和PTYs的更多背景,请参阅:

How to create a pseudo-tty for reading output and writing to input

答案 1 :(得分:2)

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

这样的东西
ProcessBuilder pb = new ProcessBuilder('python', '-i');

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