Read from the InputStream. You can append the output to a StringBuilder:
BufferedReader reader =
new BufferedReader(new InputStreamReader(process.getInputStream()));
StringBuilder builder = new StringBuilder();
String line = null;
while ( (line = reader.readLine()) != null) {
builder.append(line);
builder.append(System.getProperty("line.separator"));
}
String result = builder.toString();
Answer from Reimeus on Stack OverflowRead from the InputStream. You can append the output to a StringBuilder:
BufferedReader reader =
new BufferedReader(new InputStreamReader(process.getInputStream()));
StringBuilder builder = new StringBuilder();
String line = null;
while ( (line = reader.readLine()) != null) {
builder.append(line);
builder.append(System.getProperty("line.separator"));
}
String result = builder.toString();
As of Java 9, we finally have a one liner:
ProcessBuilder pb = new ProcessBuilder("pwd");
Process process = pb.start();
String result = new String(process.getInputStream().readAllBytes());
The method ProcessBuilder.inheritIO will redirect your command streams in your stdin, stdout and stderr. This applies to Java 7.
String[] args = {
"java",
"-jar",
"C:\\Program Files\\Java\\lib\\enchanter-beanshell-0.6.jar"
};
ProcessBuilder builder = new ProcessBuilder(args);
Start with breaking up the arguments as above. Then implement all the recommendations of When Runtime.exec() won't.
You don't want that call to waitFor() since it waits until the process is destroyed. You also don't want to read for as long as the InputStream is open, since such a read would terminate only when the process is killed.
Instead, you can simply start the process, and then wait for 7 seconds. Once 7 seconds have passed, read the available data in the buffer without waiting for the stream to close:
CopyBufferedReader stdInput = new BufferedReader(new InputStreamReader(p.getInputStream()));
Thread.sleep(7000); //Sleep for 7 seconds
while (stdInput.ready()) { //While there's something in the buffer
//read & print - replace with a buffered read (into an array) if the output doesn't contain CR/LF
System.out.println(stdInput.readLine());
}
p.destroy(); //The buffer is now empty, kill the process.
If the process keeps printing, so stdInput.ready() always returns true you can try something like this:
CopyBufferedReader stdInput = new BufferedReader(new InputStreamReader(p.getInputStream()));
char[] buffer = new char[16 * 1024]; // 16 KiB buffer, change size if needed
long startedReadingAt = System.currentTimeMillis(); //When did we start reading?
while (System.currentTimeMillis() - startedReadingAt < 7000) { //While we're waiting
if (stdInput.ready()){
int charsRead = stdInput.read(buffer); //read into the buffer - don't use readLine() so we don't wait for a CR/LF
System.out.println(new String(buffer, 0, charsRead)); //print the content we've read
} else {
Thread.sleep(100); // Wait for a while before we try again
}
}
p.destroy(); //Kill the process
In this solution, instead of sleeping, the thread spends the next 7 seconds reading from the InputStream, then it closes the process.
java.io.IOException: Stream closed That is probably because you're calling temp.readLine() after p.destroy().
The problem seems to be that the process doesn't add a new line termination after the part that you want to retrieve. To fix that, you can read the output from the program in small chunks instead of line by line.
Here is an example:
Copytry(InputStream is = p.getInputStream()){
byte[] buffer = new byte[10];
String content = "";
for(int r = 0; (r = is.read(buffer))!=-1;){
content+=new String(buffer, 0, r);
if(content.equals("all content")){// check if all the necessary data was retrieved.
p.destroy();
break;
}
}
}catch(Exception e){
e.printStackTrace();
}
If you know the exact format of the program's output, a better solution will be to use a scanner.
Copytry(Scanner sc = new Scanner(p.getInputStream())){
while(sc.hasNext()){
String word = sc.next();
}
}catch(Exception e){
e.printStackTrace();
}
The example above will read the program's output one word at a time.
From your comments it seems that the problem is that the process never terminates by itself and your program never exits the while loop.
To fix that, you can use a timer to destroy the process after a period of time, here is an example:
CopyTimer t = new Timer();
t.schedule(new TimerTask() {
@Override
public void run() {
p.destroy();
t.cancel();
t.purge();
}
}, 7000);
try(BufferedReader stdInput = new BufferedReader(new InputStreamReader(p.getInputStream()));){
while ((inputRead=stdInput.readLine()) != null){
...
}
}catch(Exception e){
e.printStackTrace();
}
Keep in mind that this approach will always cause stdInput.readLine() to throw an exception.
Use ProcessBuilder.inheritIO, it sets the source and destination for subprocess standard I/O to be the same as those of the current Java process.
CopyProcess p = new ProcessBuilder().inheritIO().command("command1").start();
If Java 7 is not an option
Copypublic static void main(String[] args) throws Exception {
Process p = Runtime.getRuntime().exec("cmd /c dir");
inheritIO(p.getInputStream(), System.out);
inheritIO(p.getErrorStream(), System.err);
}
private static void inheritIO(final InputStream src, final PrintStream dest) {
new Thread(new Runnable() {
public void run() {
Scanner sc = new Scanner(src);
while (sc.hasNextLine()) {
dest.println(sc.nextLine());
}
}
}).start();
}
Threads will die automatically when subprocess finishes, because src will EOF.
For Java 7 and later, see Evgeniy Dorofeev's answer.
For Java 6 and earlier, create and use a StreamGobbler:
CopyStreamGobbler errorGobbler =
new StreamGobbler(p.getErrorStream(), "ERROR");
// any output?
StreamGobbler outputGobbler =
new StreamGobbler(p.getInputStream(), "OUTPUT");
// start gobblers
outputGobbler.start();
errorGobbler.start();
...
Copyprivate class StreamGobbler extends Thread {
InputStream is;
String type;
private StreamGobbler(InputStream is, String type) {
this.is = is;
this.type = type;
}
@Override
public void run() {
try {
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String line = null;
while ((line = br.readLine()) != null)
System.out.println(type + "> " + line);
}
catch (IOException ioe) {
ioe.printStackTrace();
}
}
}
Here is an opinion on how to capture the standard output of a system command process into a string container.
Adapted from the web:
try {
ProcessBuilder pb = new ProcessBuilder("echo", "dummy io");
final Process p=pb.start();
BufferedReader br=new BufferedReader(new InputStreamReader(p.getInputStream()));
String line;
StringBuilder sb = new StringBuilder();
while((line=br.readLine())!=null) sb.append(line);
}
System.out.println(sb.toString());
In congruence with my original comment on what would be a good example of Basic I/O. I hacked out some code, with a few more features than basic.
Extras
- An environment shell for variables and
- A working directory
These features add "profile-style" execution to your System commands.
Foundational Work
Java Threading and Joining by Oracle.
Code
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;
/**
* Created by triston on 11/2/17.
*/
public class Commander {
private Commander(){} // no construction
public static class StreamHandler implements Runnable {
Object source;
Object destination;
StreamHandler(Object source, Object oDestination) {
this.source = source; this.destination = oDestination;
}
public void run() {
if (source instanceof InputStream) {
BufferedReader br = new BufferedReader(new InputStreamReader((InputStream) source));
String line;
try {
while ((line = br.readLine()) != null) ((StringBuilder) destination).append(line + '\n');
} catch (IOException oE) {
}
} else {
PrintWriter pw = new PrintWriter((OutputStream)destination);
pw.print((String)source);
pw.flush(); pw.close();
}
}
public static Thread read(InputStream source, StringBuilder dest) {
Thread thread = new Thread(new StreamHandler(source, dest));
(thread).start();
return thread;
}
public static Thread write(String source, OutputStream dest) {
Thread thread = new Thread(new StreamHandler(source, dest));
(thread).start();
return thread;
}
}
static Map<String, String> environment = loadEnvironment();
static String workingDirectory = ".";
static Map<String, String> loadEnvironment() {
ProcessBuilder x = new ProcessBuilder();
return x.environment();
}
static public void resetEnvironment() {
environment = loadEnvironment();
workingDirectory = ".";
}
static public void loadEnvirons(HashMap input) {
environment.putAll(input);
}
static public String getEnviron(String name) {
return environment.get(name);
}
static public void setEnviron(String name, String value) {
environment.put(name, value);
}
static public boolean clearEnviron(String name) {
return environment.remove(name) != null;
}
static public boolean setWorkingDirectory(String path) {
File test = new File(path);
if (!test.isDirectory()) return false;
workingDirectory = path;
return true;
}
static public String getWorkingDirectory() {
return workingDirectory;
}
static public class Command {
ProcessBuilder processBuilder = new ProcessBuilder();
Process process;
public Command(String... parameters) {
processBuilder.environment().putAll(environment);
processBuilder.directory(new File(workingDirectory));
processBuilder.command(parameters);
}
public int start(String input, StringBuilder output, StringBuilder error) throws IOException {
// start the process
process = processBuilder.start();
// start the error reader
Thread errorBranch = StreamHandler.read(process.getErrorStream(), error);
// start the output reader
Thread outputBranch = StreamHandler.read(process.getInputStream(), output);
// start the input
Thread inputBranch = StreamHandler.write(input, process.getOutputStream());
int rValue = 254;
try {
inputBranch.join(); rValue--;
outputBranch.join(); rValue--;
errorBranch.join(); rValue--;
return process.waitFor();
} catch (InterruptedException oE) {
oE.printStackTrace();
return rValue;
}
}
}
Testing
@Test public void foo() {
Command cmd = new Command("sh", "--");
StringBuilder output = new StringBuilder();
StringBuilder error = new StringBuilder();
int pValue = 127;
try {
pValue = cmd.start("echo well done > /dev/stderr\n\necho oh, wow; false", output, error);
} catch (IOException oE) {
}
System.out.println("output: "+output.toString());
System.out.println("error: "+error.toString());
System.out.println("\nExit code: "+pValue);
System.exit(pValue);
}
Bring your own package and JUnit annotations. This sample code demonstrates return value, command input, command standard output, and command error output.
My original design, called for the main thread to perform the standard output processing.
Have a great day.
The problem I see here is that you create a process running a shell (OK), get hold of the input and output streams of that process (OK), read a command from the file (OK) and feed it to the process (OK). Then you keep reading output lines, which succeeds while the the first Java program executes and produces output.
Then the
while ((output = processOutput.readLine()) != null) { ...
blocks as there is neither another line nor EOF.
You can fix this by spawning a thread to read and print processOutput.
Another option (which I'd very much prefer) is to create one process per command. As far as I can see you don't even need a shell: you could execute java SolveProblem ... right away. (Unices have been built for efficient subprocess creation, and don't think the shell does it differently, so there's no additional overhead to be afraid of.)
Just two hints for calling java without a shell: make sure to use the full path name and split the command line into tokens.
Edit And here it is, just using a String[] instead of your text file containing commands.
for( String cmd: new String[]{ "java Y aaa", "java Y bbb","java Y ccc" } ){
String[] toks = cmd.split( "\\s" );
ProcessBuilder builder = new ProcessBuilder( toks );
Process p = builder.start();
// get output from the process
InputStream is = p.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader processOutput = new BufferedReader(isr);
InputStream errorStream = p.getErrorStream();
InputStreamReader inputStreamReader = new InputStreamReader(errorStream);
BufferedReader processErrorOutput = new BufferedReader(inputStreamReader);
System.out.println("Executing " + cmd);
String output;
while( processErrorOutput.ready() &&
(output = processErrorOutput.readLine()) != null) {
System.out.println(output);
}
while ((output = processOutput.readLine()) != null) {
System.out.println(output);
}
processErrorOutput.close();
processOutput.close();
}
Output (silly Y prints argument three times):
Executing java Y aaa 1aaa 2aaa 3aaa Executing java Y bbb 1bbb 2bbb 3bbb Executing java Y ccc 1ccc 2ccc 3ccc
Another Edit If a marker line is inserted into the process output and the read loop checks the lines for this marker, the program can be used as it is (except for some corrections for closing processInput):
processInput.write(command + "; echo xxxEOFxxx");
//...
while ((output = processOutput.readLine()) != null
&& ! "xxxEOFxxx".equals(output)) {
System.out.println(output);
}
Although I dislike the use of "magic strings" in this way it may be permissible here as you know the set of output lines of program Solve.
And yet better is to run error and stream reading in separate threads in order to provide make program able to do something else, terminate process for instance