是否可以使用 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
>>>
Run Code Online (Sandbox Code Playgroud)
现在,主要目标是通过使用 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
Run Code Online (Sandbox Code Playgroud)
我知道调用该start方法会产生一个新进程及其执行环境。写入python2进程的输出流会导致创建另一个进程。这就是问题开始的时候。我一直无法找到将命令发送2+2到Python解释器(这是CMD的子进程)而不是其父进程的方法。
总结一下:如何运行Python解释器,在其中执行一些命令,最后将结果打印到标准输出?
Python 可执行文件可以判断您正在以非交互方式运行该命令。一旦它意识到它正在以非交互方式运行,它将不再尝试与您交互;如果没有人在那里,为什么还要费力打印到标准输出或从标准输入读取呢?
要查看这是真的,您可以尝试运行例如“ls”或“ps”并查看它们在您的程序中工作,但然后运行例如“ftp”或“telnet”或“python”并查看它们不起作用并且什么也不输出。
用 Linux 的话说,问题是我们运行进程的方式没有给它们附加 TTY。解决方案是通过创建 PTY 来欺骗他们相信另一端有 TTY。
在:
以下作品,尽管以一种非常丑陋的方式:
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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
我不会这样做。相反,我会使用线程来保持交互并避免读取阻塞并执行预期的操作,即在写出之前等待某些提示。在上面的代码中,我盲目地睡觉,然后祈祷最好的结果。
不过,我注意到您正在使用 Windows,因为您已经运行了“cmd”。抱歉,我不知道如何在 Windows 上创建 PTY。我认为您可以获得 Windows 版的 Expect;unbuffer 是 Expect 中的一个实用程序:
http://expect.sourceforge.net/
或者尝试 cygwin,但我还没有测试过这个。
有关 TTY 和 PTY 的更多背景信息,请参阅:
Asim Ihsan 的答案有正确的根本原因 - python 会知道它没有连接到交互式终端,并且不会按照您想要的方式运行。-i但是,您可以通过在启动 python 时传递该标志来使 python 以交互模式运行。这比 Asim 的建议更简单。你应该直接启动python(不需要cmd先启动的中介)。你会使用类似的东西
ProcessBuilder pb = new ProcessBuilder('python', '-i');
Run Code Online (Sandbox Code Playgroud)
然后或多或少地按照原来的问题进行。启动进程,获取关联的流,并对流进行读取和写入。