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

13

210

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.

Brinn answered 19/4, 2011 at 2:51 Comment(0)
G
314

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.

Gabbi answered 19/4, 2011 at 3:5 Comment(7)
do you know how to get the output of multiple commands at once, such as "pwd && ls"V2
@AlbertChen pwd && ls is not just executing a single file, when you do that in a shell it executes both the /bin/pwd and /bin/ls executables. If you want to do stuff like that within java you'll need to do something like {"/bin/bash","-c", "pwd && ls"}. You probably don't have the question anymore but other people might so I thought I might answer it.Rung
I think reading the two streams must be happening concurrently because if ,like in your case, the output of stdStream will fill the buffer, you wont be able to read the error stream..Demonolatry
Li3ro is partially right. The program you are listening to has 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 reading the other. The program you are listening to will then block trying to write to the filled buffer, while on the other end your program will block trying to read from a buffer that will never return EOF. You must read from both streams concurrently.Rampart
@Rampart Then why is Li3ro "partially" right? Isn't Li3ro just completely and entirely right? In this case, I don't understand why a wrong answer is hanging around here since 2011 and why it has over 200 upvotes... That's confusing.Macadamia
@AndreyTyukin You are right. All the current answers are vulnerable to deadlocks. I encourage them to downvote them to allow other answers to gain visibility. I posted a new answer for your review: https://mcmap.net/q/126212/-java-runtime-getruntime-getting-output-from-executing-a-command-line-program. Hopefully I got this right...Rampart
Awesome. Also, don't forget to close both readers stdInput.close() ...Hiltan
R
85

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;
}
Rung answered 17/12, 2013 at 2:38 Comment(4)
Thanks for the nice answer. Why is "\\A" the delimiter?Disquisition
I'm don't completely remember what my logic was when I originally wrote this. I have been using this solution for a while but I think it was because \A in a regex means beginning of string and I had to escape the slash.Rung
"\A" is the bell character. "^" is the start of a string in regex, and "$" is the end of a string in regex. This is a character you would expect not to see. The default delimiter is whitespace, according to the Java documentation, so doing this would probably spit out the full result of the command.Ahumada
\A enables multi-line matching, returning the entire text.Divert
R
25

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());
Radical answered 28/9, 2018 at 11:40 Comment(0)
R
18

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.

Rampart answered 16/9, 2019 at 2:35 Comment(2)
Oh, wow! I didn't expect that you'd reply on the comment so quickly, let alone answer this eternally old question! :) I'm now considering starting a bounty. Will take a look at your answer later. Thanks!Macadamia
With JDK10 you can make it shorter with result = new StringWriter(80) and in.transferTo(result) in place of a loop. I've added answer which shows background thread handling.Orientate
R
12

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);
    }
Rowenarowland answered 12/3, 2018 at 22:54 Comment(0)
R
8

@Senthil and @Arend answer (https://mcmap.net/q/126212/-java-runtime-getruntime-getting-output-from-executing-a-command-line-program) 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);
Revet answered 7/8, 2016 at 4:55 Comment(0)
M
2

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()
Monetmoneta answered 10/11, 2019 at 18:36 Comment(0)
D
2

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;
Dunleavy answered 5/5, 2021 at 9:0 Comment(0)
S
1
Process p = Runtime.getRuntime().exec("ping google.com");

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

p.getErrorStream().transferTo(System.out);
Sibship answered 13/8, 2020 at 20:15 Comment(3)
Could you provide an explanation for your answer? How does this solve the question being asked?Guildroy
This works in much later versions of Java.Porterporterage
process.errorStream.transferTo(System.err) to have a better console ;)Gourmandise
F
0

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);
}
Frilling answered 6/4, 2016 at 3:58 Comment(1)
Rlog.i... I presume that's some logging framework.. Which one - something you created yourself?Gruchot
R
0

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;
       }
Revisionist answered 18/10, 2019 at 17:39 Comment(0)
O
0

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;
}
Orientate answered 30/4, 2023 at 18:19 Comment(0)
F
-1

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.

Farlee answered 14/7, 2016 at 11:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.