The Python executable can tell that you are running the command
non-interactively. Once it realises it's being run non-interactively it
will no longer attempt to interact with you; why bother printing to
stdout or reading from stdin if noone is there?
To see that this is true you would attempt to run e.g. "ls" or "ps" and
see that they work in your program, but then run e.g. "ftp" or "telnet"
or "python" and see that don't work and output nothing.
In the parlance of Linux the problem is that the way we're running
processes does not attach a TTY to them. The solution is to trick them
into believing there's a TTY on the other end by creating a PTY.
Trick an application into thinking its stdin is interactive, not a pipe
On:
- my Mac OS X 10.9.4 laptop with CPython 2.7.5 and Java 1.8.0_05 and
- a Ubuntu 12.04.4 LTS server with CPython 2.7.5 and Java 1.7.0_55
the following works, albeit in a very ugly fashion:
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;
}
}
}
I wouldn't do it like this. Instead I'd use threads to stay interactive
and avoid blocking on reads and do what expect does, which is wait for
certain prompts before writing out. In the code above I blindly sleep
then hope for the best.
However I notice that you're using Windows, since you've run "cmd".
I don't know how to create PTYs on Windows, sorry. I think though
that you can get Expect for Windows; unbuffer is a utility within
Expect:
http://expect.sourceforge.net/
Or try cygwin, but again I haven't tested this.
For further background on TTYs and PTYs see:
How to create a pseudo-tty for reading output and writing to input