【问题标题】:How to run the Python interpreter and get its output using Java?如何运行 Python 解释器并使用 Java 获取其输出?
【发布时间】:2020-05-10 22:49:20
【问题描述】:

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

【问题讨论】:

  • 您的方法不容易实现,但多线程可能可行。我认为使用jython 可能会更容易。
  • @ElliottFrisch:你能用几句话解释为什么我的代码不能按预期工作吗?
  • 您找不到将命令 2+2 发送到 Python 解释器的方法。不要运行cmd,只需运行python2。添加线程。或者使用 jython。或者在此处添加更多详细信息,说明您为什么希望能够控制cmd 来控制子进程。提示,我认为你不能。
  • @ElliottFrisch:我尝试直接运行python2,但没有得到任何输出。很抱歉打扰你,但为什么我需要线程呢?非常感谢您的宝贵时间。
  • 因为你想让它工作。相信你可以这样想,你正在运行的进程是在JVM外部的。因此,您需要一个线程来处理该进程的输出,并需要另一个线程来为该外部进程提供输入。

标签: java python


【解决方案1】:

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

要看到这是真的,你会尝试运行例如“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,抱歉。我认为虽然 你可以得到 Windows 的 Expect; unbuffer 是一个实用程序 期望:

http://expect.sourceforge.net/

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

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

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

【讨论】:

  • 感谢您的回答。你知道我在哪里可以找到/usr/bin/script 的源代码吗?我真的很感兴趣它是如何工作的。是否有可能在 Java for Windows 中做类似的事情?
  • @Luka 我认为可以在此处找到脚本源代码的示例:goo.gl/8YD1fP 但我不确定。看看我的回答中关于forkpty() 的最后一个链接(在Windows 上不存在)。对于 Java,可以尝试“expect4j”(github.com/cverges/expect4j)或“expectit”(github.com/Alexey1Gavrilov/expectit),或者如果您想要不是用 Java 编写的实用程序,您可以从 Java 搜索“expect”、“tcl expect windows”、“期待”。祝你好运!
  • 您能否修改您的代码以使用您在回答中提到的线程?我不能让它和他们一起工作。我总是被第一个阅读语句阻塞(程序永远挂起)。谢谢。
  • @Luka 我认为最好开始一个新问题,发布您拥有的代码,然后我会看看它。由于这个问题已经得到了一个公认的答案,因此开始一个新问题是更好的礼仪。创建新问题后,请在此处发布 URL,我将再次尝试线程解决方案。
  • 经过一番努力和研究后,我决定在这里创建一个新问题:stackoverflow.com/questions/25472013/…。如果你有时间,请看看。提前致谢。
【解决方案2】:

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

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多