简体   繁体   中英

java runtime exec a python script with raw_input

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 (eg 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM