I have a java program that runs a python script (The script is not mine and so cannot be changed). I'm running the script with:
Process p = Runtime.getRuntime().exec("python myScript.py");
The script has a "raw_input" line that expects the user input. I tried using
BufferedWriter userInput = new BufferedWriter(new OutputStreamWriter(p.getOutputStream()));
userInput.write("myInput");
But that doesn't seem to work;
Another thing, I'm reading the output with
BufferedReader stdInput = new BufferedReader(new
InputStreamReader(p.getInputStream()));
List<String> output = new ArrayList();
while ((s = stdInput.readLine()) != null) {
output.add(s);
}
This works when the python script is not expecting any input, but when there's an input_raw(), the stdInput.readLine() is just stuck.
An example of the python script:
name = raw_input("What is your name? ")
print "your name is "+name
The whole program:
public static void main(String args[]) {
PythonRunner pr = new PythonRunner();
pr.start();
while(!pr.isFinished){
try {
System.out.println("waiting...");
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
for(String s:pr.result) System.out.println(s);
}
public class PythonRunner extends Thread {
public List<String> result;
public boolean isFinished;
public PythonRunner() {
result= new ArrayList<>();
isFinished = false;
}
@Override
public void run(){
try {
Process p = Runtime.getRuntime().exec("python myScript.py");
BufferedWriter userInput = new BufferedWriter(new OutputStreamWriter(p.getOutputStream()));
BufferedReader stdInput = new BufferedReader(new
InputStreamReader(p.getInputStream()));
userInput.write("myInput");
String s;
while ((s = stdInput.readLine()) != null) {
result.add(s);
}
isFinished=true;
}
catch (IOException e) {
isFinished=true;
}
}
}
EDIT: I managed to give the script the input using
userInput.write(cmd);
userInput.newLine();
userInput.flush();
However, I'm still having trouble reading the output. Some of the scripts have an infinite loop of input-> print. for example, the script:
stop = False
while not stop:
name = raw_input("")
print "your name is "+name
if name == "stop":
stop = True
While this script runs, I can't seem to be able to read the output of the already given names.. Only when the process is given the "stop" command, can I read the whole output.
You need to inherit IO
https://docs.oracle.com/javase/7/docs/api/java/lang/ProcessBuilder.html#inheritIO()
Sets the source and destination for subprocess standard I/O to be the same as those of the current Java process. This is a convenience method. An invocation of the form
pb.inheritIO()
Then, your Process execution can take following form
public class ProcessSample {
public static void main(String [] arg) throws Exception {
ProcessBuilder pb =
new ProcessBuilder("python", "script.py").inheritIO();
Process p = pb.start();
p.waitFor();
}
}
and, with your script
name = raw_input("What is your name? ")
print "your name is "+name
you can do
> javac ProcessSample.java
> java -cp . ProcessSample
What is your name? a
your name is a
Inheriting IO for stdout
You can always pass some values to Python code while at the same time read values from stdout - by inheriting it.
import java.io.*;
public class PythonProcessRedirect {
public static void main(String [] arg) throws Exception {
ProcessBuilder pb =
new ProcessBuilder("python", "script_raw.py");
pb.redirectErrorStream(true);
pb.redirectOutput(ProcessBuilder.Redirect.INHERIT);
Process p = pb.start();
BufferedWriter writer = new BufferedWriter( new OutputStreamWriter( p.getOutputStream() ));
String input = "2\n";
writer.write(input);
writer.flush();
p.waitFor();
}
}
Note that for continuous flow of data you need to flush it
import sys
import time
sys.stdout.write("give some input: ")
sys.stdout.flush()
time.sleep(2)
line = sys.stdin.readline()
sys.stdout.write("you typed: " + line)
sys.stdout.flush();
otherwise, data will be available not sooner than Python flushes it (e.g. by filling buffer up to the limit).
Stream Gobbler
You can also go with Stream Gobbler and run separate threads for stdin/stdout/stderr.
import java.io.*;
public class PythonProcessStreamGobblerIO {
public static void main(String [] arg) throws Exception {
final Process p = Runtime.getRuntime().exec("python ./script_os.py" );
new Thread() {
public void run() {
try {
BufferedWriter writer = new BufferedWriter( new OutputStreamWriter( p.getOutputStream() ));
String input = "2\n";
writer.write(input);
writer.flush();
} catch(Exception ex) {
ex.printStackTrace();
}
}
}.start();
new Thread() {
public void run() {
try {
Reader reader = new InputStreamReader(p.getInputStream());
int data = -1;
while((data =reader.read())!= -1){
char c = (char) data;
System.out.print(c);
}
reader.close();
} catch(Exception ex) {
ex.printStackTrace();
}
}
}.start();
p.waitFor();
}
}
But, again, you have to make sure (on Python side) that stdout is flushed.