user541597
user541597

Reputation: 4355

Java Runtime.getRuntime(): getting output from executing a command line program

I'm using the runtime to run command prompt commands from my Java program. However, I'm not aware of how I can get the output the command returns.

Here is my code:

Runtime rt = Runtime.getRuntime();

String[] commands = {"system.exe", "-send" , argument};

Process proc = rt.exec(commands);

I tried doing System.out.println(proc); but that did not return anything. The execution of that command should return two numbers separated by a semicolon. How could I get this in a variable to print out?

Here is the code I'm using now:

String[] commands = {"system.exe", "-get t"};

Process proc = rt.exec(commands);

InputStream stdIn = proc.getInputStream();
InputStreamReader isr = new InputStreamReader(stdIn);
BufferedReader br = new BufferedReader(isr);

String line = null;
System.out.println("<OUTPUT>");

while ((line = br.readLine()) != null)
     System.out.println(line);

System.out.println("</OUTPUT>");
int exitVal = proc.waitFor();
System.out.println("Process exitValue: " + exitVal);

But I'm not getting anything as my output, but when I run that command myself it works fine.

Upvotes: 214

Views: 384929

Answers (13)

Senthil
Senthil

Reputation: 5824

Here is the way to go:

Runtime rt = Runtime.getRuntime();
String[] commands = {"system.exe", "-get t"};
Process proc = rt.exec(commands);

BufferedReader stdInput = new BufferedReader(new 
     InputStreamReader(proc.getInputStream()));

BufferedReader stdError = new BufferedReader(new 
     InputStreamReader(proc.getErrorStream()));

// Read the output from the command
System.out.println("Here is the standard output of the command:\n");
String s = null;
while ((s = stdInput.readLine()) != null) {
    System.out.println(s);
}

// Read any errors from the attempted command
System.out.println("Here is the standard error of the command (if any):\n");
while ((s = stdError.readLine()) != null) {
    System.out.println(s);
}

Read the Javadoc for more details here. ProcessBuilder would be a good choice to use.

Upvotes: 321

DuncG
DuncG

Reputation: 15196

To reliably start a sub-process you need to handle the output streams at same time or the process will block when either STDOUT or STDERR is not consumed when they fill to the default buffer limit.

You can demonstrate this issue by these test commands which write large amount of data to STDOUT and STDERR at same pace. If your app does not keep up with reading from both of these streams then the sub-process will freeze / deadlock:

// WINDOWS:
String[] commands = {"cmd.exe", "/c", "FOR /L %X IN (1, 1, 10000) DO echo Hello STDOUT %X && echo Hello STDERR %X 1>&2"};
// Linux / Unix style OS
String[] commands = {"/bin/bash", "-c", "for i in {1..10000} ; do echo Hello STDERR $i 1>&2 ; echo Hello STDOUT $i; done"};

You can can avoid the problem by using ProcessBuilder which gives better control of where output streams go, and prevent deadlock situation by calling pb.redirectErrorStream(true) or pb.inheritIO() or redirect either of STDOUT / STDERR to File using pb.redirectOutput/Error(file) / or use different threads for reading from STDOUT and STDERR.

Here is a simple example of how to handle launch which could be used in place of Runtime.exec() and sends STDOUT(/STDERR) to any stream you pass in, and which avoids the deadlock situation:

// Example: 
start(command, null, System.out, null);
// or 
start(command, null, System.out, System.err);
// Don't forget to close streams you pass in - if appropriate

public static int start(String[] cmd, byte[] stdin, OutputStream stdout, OutputStream stderr)
        throws IOException, InterruptedException
{
    Objects.requireNonNull(cmd);
    Objects.requireNonNull(stdout);
    System.out.println("start "+Arrays.toString(cmd));

    // Launch and wait:
    ProcessBuilder pb = new ProcessBuilder(cmd);
    if (stderr == null) {
        pb.redirectErrorStream(true);   // No STDERR => merge to STDOUT
    }

    Process p = pb.start();

    // Consumes STDERR at same time as STDOUT, not doing this large streams can block I/O in the sub-process
    Thread bg = null;
    if (stderr != null) {
        Runnable task = () -> {
            try(var from = p.getErrorStream()) {
                from.transferTo(stderr);
            } catch(IOException io) {
                throw new UncheckedIOException(io);
            }
        };
        bg = new Thread(task, "STDERR");
        bg.start();
    }

    // Send STDIN if required, and close STDIN stream
    // NOTE!!! a huge input stream can lock up STDOUT/STDERR readers, you may need a background thread here too
    try(OutputStream os = p.getOutputStream()) {
        if (stdin != null) os.write(stdin);
    }

    // Move STDOUT to the output stream
    try(var stdo = p.getInputStream()) {
        stdo.transferTo(stdout);
    }

    int rc = p.waitFor();
    if (bg != null) {
        bg.join();
    }

    System.out.println("start "+Arrays.toString(cmd));
    System.out.println("Exit "+p.pid()+" CODE "+rc +' '+(rc == 0 ? "OK":"**** ERROR ****")+" "+(stderr == null ? "STDERR>OUT":""));
    return rc;
}

Upvotes: 1

Afanasov S
Afanasov S

Reputation: 333

Create class :

public class Utils {
public static final String SHEL_EXECUTE_ERROR = "SHEL_EXECUTE_ERROR";
public static String shellExec(String cmdCommand) {
    final StringBuilder stringBuilder = new StringBuilder();
    try {
        final Process process = Runtime.getRuntime().exec(cmdCommand);
        final BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
        String line;
        while ((line = bufferedReader.readLine()) != null) {
            stringBuilder.append(line);
        }

    } catch (Exception e) {
        return SHEL_EXECUTE_ERROR;
    }
    return stringBuilder.toString();
}

}

and use:

final String shellExec = shellExec("cmd /c ver");
final String versionOS = shellExec.equals(SHEL_EXECUTE_ERROR) ? "empty" : shellExec;

Upvotes: 2

735Tesla
735Tesla

Reputation: 3261

A quicker way is this:

public static String execCmd(String cmd) throws java.io.IOException {
    java.util.Scanner s = new java.util.Scanner(Runtime.getRuntime().exec(cmd).getInputStream()).useDelimiter("\\A");
    return s.hasNext() ? s.next() : "";
}

Which is basically a condensed version of this:

public static String execCmd(String cmd) throws java.io.IOException {
    Process proc = Runtime.getRuntime().exec(cmd);
    java.io.InputStream is = proc.getInputStream();
    java.util.Scanner s = new java.util.Scanner(is).useDelimiter("\\A");
    String val = "";
    if (s.hasNext()) {
        val = s.next();
    }
    else {
        val = "";
    }
    return val;
}

I know this question is old but I am posting this answer because I think this may be quicker.

Edit (For Java 7 and above)

Need to close Streams and Scanners. Using AutoCloseable for neat code:

public static String execCmd(String cmd) {
    String result = null;
    try (InputStream inputStream = Runtime.getRuntime().exec(cmd).getInputStream();
            Scanner s = new Scanner(inputStream).useDelimiter("\\A")) {
        result = s.hasNext() ? s.next() : null;
    } catch (IOException e) {
        e.printStackTrace();
    }
    return result;
}

Upvotes: 88

Saptarsi Halder
Saptarsi Halder

Reputation: 21

Process p = Runtime.getRuntime().exec("ping google.com");

p.getInputStream().transferTo(System.out);

p.getErrorStream().transferTo(System.out);

Upvotes: 1

android developer
android developer

Reputation: 116040

If you write on Kotlin, you can use:

val firstProcess = ProcessBuilder("echo","hello world").start()
val firstError = firstProcess.errorStream.readBytes().decodeToString()
val firstResult = firstProcess.inputStream.readBytes().decodeToString()

Upvotes: 2

Sergio Mcfly PYK
Sergio Mcfly PYK

Reputation: 4331

Pretty much the same as other snippets on this page but just organizing things up over an function, here we go...

String str=shell_exec("ls -l");

The Class function:

public String shell_exec(String cmd)
       {
       String o=null;
       try
         {
         Process p=Runtime.getRuntime().exec(cmd);
         BufferedReader b=new BufferedReader(new InputStreamReader(p.getInputStream()));
         String r;
         while((r=b.readLine())!=null)o+=r;
         }catch(Exception e){o="error";}
       return o;
       }

Upvotes: 0

Gili
Gili

Reputation: 90150

At the time of this writing, all other answers that include code may result in deadlocks.

Processes have a limited buffer for stdout and stderr output. If you don't listen to them concurrently, one of them will fill up while you are trying reading the other. For example, you could be waiting to read from stdout while the process is waiting to write to stderr. You cannot read from the stdout buffer because it is empty and the process cannot write to the stderr buffer because it is full. You are each waiting on each other forever.

Here is a possible way to read the output of a process without a risk of deadlocks:

public final class Processes
{
    private static final String NEWLINE = System.getProperty("line.separator");

    /**
     * @param command the command to run
     * @return the output of the command
     * @throws IOException if an I/O error occurs
     */
    public static String run(String... command) throws IOException
    {
        ProcessBuilder pb = new ProcessBuilder(command).redirectErrorStream(true);
        Process process = pb.start();
        StringBuilder result = new StringBuilder(80);
        try (BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream())))
        {
            while (true)
            {
                String line = in.readLine();
                if (line == null)
                    break;
                result.append(line).append(NEWLINE);
            }
        }
        return result.toString();
    }

    /**
     * Prevent construction.
     */
    private Processes()
    {
    }
}

The key is to use ProcessBuilder.redirectErrorStream(true) which will redirect stderr into the stdout stream. This allows you to read a single stream without having to alternate between stdout and stderr. If you want to implement this manually, you will have to consume the streams in two different threads to make sure you never block.

Upvotes: 19

Derek Zhu
Derek Zhu

Reputation: 183

Adapted from the previous answer:

public static String execCmdSync(String cmd, CmdExecResult callback) throws java.io.IOException, InterruptedException {
    RLog.i(TAG, "Running command:", cmd);

    Runtime rt = Runtime.getRuntime();
    Process proc = rt.exec(cmd);

    //String[] commands = {"system.exe", "-get t"};

    BufferedReader stdInput = new BufferedReader(new InputStreamReader(proc.getInputStream()));
    BufferedReader stdError = new BufferedReader(new InputStreamReader(proc.getErrorStream()));

    StringBuffer stdOut = new StringBuffer();
    StringBuffer errOut = new StringBuffer();

    // Read the output from the command:
    System.out.println("Here is the standard output of the command:\n");
    String s = null;
    while ((s = stdInput.readLine()) != null) {
        System.out.println(s);
        stdOut.append(s);
    }

    // Read any errors from the attempted command:
    System.out.println("Here is the standard error of the command (if any):\n");
    while ((s = stdError.readLine()) != null) {
        System.out.println(s);
        errOut.append(s);
    }

    if (callback == null) {
        return stdInput.toString();
    }

    int exitVal = proc.waitFor();
    callback.onComplete(exitVal == 0, exitVal, errOut.toString(), stdOut.toString(), cmd);

    return stdInput.toString();
}

public interface CmdExecResult{
    void onComplete(boolean success, int exitVal, String error, String output, String originalCmd);
}

Upvotes: 0

brahmananda Kar
brahmananda Kar

Reputation: 59

Try reading the InputStream of the runtime:

Runtime rt = Runtime.getRuntime();
String[] commands = {"system.exe", "-send", argument};
Process proc = rt.exec(commands);
BufferedReader br = new BufferedReader(
    new InputStreamReader(proc.getInputStream()));
String line;
while ((line = br.readLine()) != null)
    System.out.println(line);

You might also need to read the error stream (proc.getErrorStream()) if the process is printing error output. You can redirect the error stream to the input stream if you use ProcessBuilder.

Upvotes: 0

nidalpres
nidalpres

Reputation: 3868

@Senthil and @Arend answer (https://stackoverflow.com/a/5711150/2268559) mentioned ProcessBuilder. Here is the example using ProcessBuilder with specifying environment variables and working folder for the command:

    ProcessBuilder pb = new ProcessBuilder("ls", "-a", "-l");

    Map<String, String> env = pb.environment();
    // If you want clean environment, call env.clear() first
    //env.clear();
    env.put("VAR1", "myValue");
    env.remove("OTHERVAR");
    env.put("VAR2", env.get("VAR1") + "suffix");

    File workingFolder = new File("/home/user");
    pb.directory(workingFolder);

    Process proc = pb.start();

    BufferedReader stdInput = new BufferedReader(new InputStreamReader(proc.getInputStream()));

    BufferedReader stdError = new BufferedReader(new InputStreamReader(proc.getErrorStream()));

    // Read the output from the command:
    System.out.println("Here is the standard output of the command:\n");
    String s = null;
    while ((s = stdInput.readLine()) != null)
        System.out.println(s);

    // Read any errors from the attempted command:
    System.out.println("Here is the standard error of the command (if any):\n");
    while ((s = stdError.readLine()) != null)
        System.out.println(s);

Upvotes: 8

whirlwin
whirlwin

Reputation: 16541

If use are already have Apache commons-io available on the classpath, you may use:

Process p = new ProcessBuilder("cat", "/etc/something").start();
String stderr = IOUtils.toString(p.getErrorStream(), Charset.defaultCharset());
String stdout = IOUtils.toString(p.getInputStream(), Charset.defaultCharset());

Upvotes: 26

Jackkobec
Jackkobec

Reputation: 6755

Also we can use streams for obtain command output:

public static void main(String[] args) throws IOException {

        Runtime runtime = Runtime.getRuntime();
        String[] commands  = {"free", "-h"};
        Process process = runtime.exec(commands);

        BufferedReader lineReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
        lineReader.lines().forEach(System.out::println);

        BufferedReader errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
        errorReader.lines().forEach(System.out::println);
    }

Upvotes: 12

Related Questions