Get Command Prompt Output to String In Java
Asked Answered
F

3

8

I need a java method that will read command prompt output and store it into a String to be read into Java.

This is what I have so far but isn't working right.

public void testGetOutput() {
    System.out.println("\n\n****This is the testGetOutput Method!****");
    String s = null;
    String query = "dir " + this.desktop;
    try {
        Runtime runtime = Runtime.getRuntime();
        InputStream input = runtime.exec("cmd /c " + query).getInputStream();
        BufferedInputStream buffer = new BufferedInputStream(input);
        BufferedReader commandResult = new BufferedReader(new InputStreamReader(buffer));
        String line = "";
        try {
            while ((line = commandResult.readLine()) != null) {
                s += line + "\n";
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println(s);
    } catch (Exception e) {
        e.printStackTrace();
    }
}//end testGetOutput()

I think the problem is when I try to change the query to be a command which will execute HandBrakeCLI.exe. Looking at my system when the program is running (but seems to have paused), it shows me that HandBrakeCLI.exe is running under a cmd window which is being run under my IDE. All that makes sense, but the HandBrakeCLI.exe doesn't exit, so I'm guessing that's why I can't read the output as input to my program.

So, after that background. My big question is this: How do I get HandBrakeCLI.exe to close after it's finished with my query so I can get its output? Just for extra info, the only difference between the method above and the scan DVD method I have for HandBrakeCLI is the query variable is different. Like this example:

String query = "C:\Users\Kent\Desktop\HBCLI\HandBrakeCLI -t --scan -i "C:\Users\Kent\Desktop\General Conference DVDs\Sources\174th October 2004\DVD 1"; //this is actually a variable in the DVD object, but here's an example'

Oh, and by the way, when I run that query in a regular command prompt, it does exactly what I want it to, giving me all the output I desperately desire!

Here's the original problem (I'm not sure how to resubmit a question):

I've been looking everywhere and can't figure this out. I'm not sure what I've found is even relevant to what I want to do. I don't have a whole lot of code for it yet, so it wont do much to put code here and I think this should be pretty simple, so I'm going to give some screenshots here. So here's my task:

  1. Scan folder which is full of ripped DVD folders (Video_TS folders with VOB files etc.) and store these folder names as the title of the DVD.

  2. Scan each folder using the HandBrakeCLI and store the output to a string.

  3. Regex the string to identify each title, chapter, and language.

  4. Generate queries to give back to HandBrakeCLI to bulk encode each language in each chapter in each title for each DVD (you can see why I want to automate this!)

  5. Store these queries in a *.bat file

The only part I'm not sure about is step 2! I can do everything else pretty easily. I've read a lot about OutputStreams, but I just can't seem to understand how it works. I really just need to get the output to a string which I can regex to get the stuff I need. Here are the screenshots of what I need to input and what I need to strip from the output:

Input to HandBrakeCLI:

enter image description here

Output to scan:

enter image description here

Foul answered 3/10, 2011 at 15:43 Comment(0)
N
5

First you need a non-blocking way to read from Standard.out and Standard.err

private class ProcessResultReader extends Thread
{
    final InputStream is;
    final String type;
    final StringBuilder sb;

    ProcessResultReader(@Nonnull final InputStream is, @Nonnull String type)
    {
        this.is = is;
        this.type = type;
        this.sb = new StringBuilder();
    }

    public void run()
    {
        try
        {
            final InputStreamReader isr = new InputStreamReader(is);
            final BufferedReader br = new BufferedReader(isr);
            String line = null;
            while ((line = br.readLine()) != null)
            {
                this.sb.append(line).append("\n");
            }
        }
        catch (final IOException ioe)
        {
            System.err.println(ioe.getMessage());
            throw new RuntimeException(ioe);
        }
    }

    @Override
    public String toString()
    {
        return this.sb.toString();
    }
}

Then you need to tie this class into the respective InputStream and OutputStreamobjects.

    try
    {
        final Process p = Runtime.getRuntime().exec(String.format("cmd /c %s", query));
        final ProcessResultReader stderr = new ProcessResultReader(p.getErrorStream(), "STDERR");
        final ProcessResultReader stdout = new ProcessResultReader(p.getInputStream(), "STDOUT");
        stderr.start();
        stdout.start();
        final int exitValue = p.waitFor();
        if (exitValue == 0)
        {
            System.out.print(stdout.toString());
        }
        else
        {
            System.err.print(stderr.toString());
        }
    }
    catch (final IOException e)
    {
        throw new RuntimeException(e);
    }
    catch (final InterruptedException e)
    {
        throw new RuntimeException(e);
    }

This is pretty much the boiler plate I use when I need to Runtime.exec() anything in Java.

A more advanced way would be to use FutureTask and Callable or at least Runnable rather than directly extending Thread which isn't the best practice.

NOTE:

The @Nonnull annotations are in the JSR305 library. If you are using Maven, and you are using Maven aren't you, just add this dependency to your pom.xml.

<dependency>
  <groupId>com.google.code.findbugs</groupId>
  <artifactId>jsr305</artifactId>
  <version>1.3.9</version>
</dependency>
Noncombatant answered 11/10, 2011 at 15:46 Comment(2)
Jarrod, thanks for the quick response! I'll try to implement this right away and let you know how it goes!Foul
Perfect! I'm not exactly sure how it works. But I'll look at it a little bit to figure it out. But yeah, it worked like a charm! Now I just have to regex all that stuff! Anyway, thanks a ton for your help! Oh, and I'm not sure what Maven is. My IDE is Netbeans. I didn't need to add the <dependency> stuff. Thanks again!Foul
T
7

This full Java program example runs the command 'dir' (directory listing) on the command line and pulls the result into a String and prints it on the console.

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

public class X {
    public static void main(String[] args) {
        try{
            String command = "dir";
            String s = get_commandline_results(command);
            System.out.println(s);
        }
        catch(Exception e){
            e.printStackTrace();
        }
        System.out.println("done");
    }

    public static String get_commandline_results(String cmd)
        throws IOException, InterruptedException, IllegalCommandException{

        //Do not remove the authorizedCommand method.  Be prepared 
        //to lose your hard drive if you have not white-listed the 
        //commands that can run.
        if (!authorizedCommand(cmd)) 
            throw new IllegalCommandException();

        String result = "";
        final Process p = Runtime.getRuntime().
            exec(String.format("cmd /c %s", cmd));
        final ProcessResultReader stderr = new ProcessResultReader(
                p.getErrorStream(), "STDERR");
        final ProcessResultReader stdout = new ProcessResultReader(
                p.getInputStream(), "STDOUT");
        stderr.start();
        stdout.start();
        final int exitValue = p.waitFor();
        if (exitValue == 0){
            result = stdout.toString();
        }
        else{
            result = stderr.toString();
        }
        return result;
    }
    public static boolean authorizedCommand(String cmd){
        //Do not allow any command to be run except for the ones 
        //that we have pre-approved here.  This lessens the 
        //likelihood that fat fingers will wreck your computer.
        if (cmd.equals("dir"))
            return true;
        //add the commands you want to authorize here.

        return false;
    }
}

class ProcessResultReader extends Thread{
    final InputStream is;
    final String type;
    final StringBuilder sb;

    ProcessResultReader(final InputStream is, String type){
        this.is = is;
        this.type = type;
        this.sb = new StringBuilder();
    }

    public void run()
    {
        try{
            final InputStreamReader isr = new InputStreamReader(is);
            final BufferedReader br = new BufferedReader(isr);
            String line = null;
            while ((line = br.readLine()) != null)
            {
                this.sb.append(line).append("\n");
            }
        }
        catch (final IOException ioe)
        {
            System.err.println(ioe.getMessage());
            throw new RuntimeException(ioe);
        }
    }
    @Override
    public String toString()
    {
        return this.sb.toString();
    }
}
class IllegalCommandException extends Exception{
    private static final long serialVersionUID = 1L;
    public IllegalCommandException(){   }
}

On Windows, this is the result I get

Directory of D:\projects\eric\eclipseworkspace\testing2
07/05/2012  01:06 PM    <DIR>          .
07/05/2012  01:06 PM    <DIR>          ..
06/05/2012  11:11 AM               301 .classpath
06/05/2012  11:11 AM               384 .project
06/05/2012  11:11 AM    <DIR>          .settings
07/05/2012  01:42 PM    <DIR>          bin
06/05/2012  11:11 AM    <DIR>          src
07/05/2012  01:06 PM             2,285 usernames.txt
               3 File(s)          2,970 bytes
               5 Dir(s)  45,884,035,072 bytes free

done
Tittup answered 5/7, 2012 at 17:56 Comment(1)
+1 for posting another workable solution for generations to come.Foul
N
5

First you need a non-blocking way to read from Standard.out and Standard.err

private class ProcessResultReader extends Thread
{
    final InputStream is;
    final String type;
    final StringBuilder sb;

    ProcessResultReader(@Nonnull final InputStream is, @Nonnull String type)
    {
        this.is = is;
        this.type = type;
        this.sb = new StringBuilder();
    }

    public void run()
    {
        try
        {
            final InputStreamReader isr = new InputStreamReader(is);
            final BufferedReader br = new BufferedReader(isr);
            String line = null;
            while ((line = br.readLine()) != null)
            {
                this.sb.append(line).append("\n");
            }
        }
        catch (final IOException ioe)
        {
            System.err.println(ioe.getMessage());
            throw new RuntimeException(ioe);
        }
    }

    @Override
    public String toString()
    {
        return this.sb.toString();
    }
}

Then you need to tie this class into the respective InputStream and OutputStreamobjects.

    try
    {
        final Process p = Runtime.getRuntime().exec(String.format("cmd /c %s", query));
        final ProcessResultReader stderr = new ProcessResultReader(p.getErrorStream(), "STDERR");
        final ProcessResultReader stdout = new ProcessResultReader(p.getInputStream(), "STDOUT");
        stderr.start();
        stdout.start();
        final int exitValue = p.waitFor();
        if (exitValue == 0)
        {
            System.out.print(stdout.toString());
        }
        else
        {
            System.err.print(stderr.toString());
        }
    }
    catch (final IOException e)
    {
        throw new RuntimeException(e);
    }
    catch (final InterruptedException e)
    {
        throw new RuntimeException(e);
    }

This is pretty much the boiler plate I use when I need to Runtime.exec() anything in Java.

A more advanced way would be to use FutureTask and Callable or at least Runnable rather than directly extending Thread which isn't the best practice.

NOTE:

The @Nonnull annotations are in the JSR305 library. If you are using Maven, and you are using Maven aren't you, just add this dependency to your pom.xml.

<dependency>
  <groupId>com.google.code.findbugs</groupId>
  <artifactId>jsr305</artifactId>
  <version>1.3.9</version>
</dependency>
Noncombatant answered 11/10, 2011 at 15:46 Comment(2)
Jarrod, thanks for the quick response! I'll try to implement this right away and let you know how it goes!Foul
Perfect! I'm not exactly sure how it works. But I'll look at it a little bit to figure it out. But yeah, it worked like a charm! Now I just have to regex all that stuff! Anyway, thanks a ton for your help! Oh, and I'm not sure what Maven is. My IDE is Netbeans. I didn't need to add the <dependency> stuff. Thanks again!Foul
A
1

Assuming you use Runtime.exec() to start your external process, giving you a Process object, there are three relevant methods on that object: getOutputStream(), getInputStream(), and getErrorStream().

I think it is easy to get confused about these -- you are probably thinking that you want to see the process's output, so therefore an "output stream" is what you want. But the thing you need to remember is that the naming of these objects is from the Java code's perspective -- an OutputStream is for Java to write output to, while an InputStream is for Java to read input from. The output of your external process is, from Java's perspective, input.

So you would use getInputStream() and call appropriate methods on the InputStream returned by it to read the output. You also may want to use getErrorStream() to ensure that you are not missing anything because it is written to standard error.

Apochromatic answered 3/10, 2011 at 15:54 Comment(7)
Thanks for the clarification! That was a bit confusing. The problem I'm facing now is instead of printing the results of the query (as seen in my second screenshot), it's printing the query itself. There's too much content in the code to add to a comment here, so here's a link to a collabedit doc: collabedit.com/5r384Foul
One issue, I think, is that exec() expects an actual executable -- a batch file is not an executable program, it is a series of command to be executed by the Windows shell. See #616448.Apochromatic
We're getting really close! So now my problem is my buffered reader object is null. I'm not sure why. I've updated the code on the collabedit doc. You'll notice I put println's around the commandResult because that's where the code pauses, and commandResult.readLine() = null when it should equal the first input line (or at least, that's what I'm looking for). Thanks again for the help! collabedit.com/5r384Foul
Ah, I think the problem is the use of start in the external command, which I didn't think anything of when I referred you to the other answer. That opens a separate command window, where the output of the command will be written -- so it does not go to the standard output of the actual cmd process. Try removing start from the command, e.g. cmd /c path-to-batch-file.bat.Apochromatic
I can't believe it. It's still not working. commandResult.readLine() is just reading the command rather than the output of the command. (like, if the command were "dir" then commandResult.readLine() would return "dir" instead of what the output of the "dir" command is). Is there any way for me to convert what the output of that command would be into a string? Here's the updated code: collabedit.com/5r384 Thanks again for all your help Dave! I know that someone smarter than me could do this! Hopefully I'll get good at this and be able to contribute to stackoverflow in good ways :)Foul
One other thing that may be helpful is that the code stops in the while loop: while ((line = commandResult.readLine()) != null) {...} It will run fine (kinda) for a few iterations (enough to print all the commands in the *.bat file), then it will stop (even when debugging, and it doesn't throw errors, it just wont move on) and NetBeans tells me it's still running. Not sure what the problem is.Foul
Well, now I have it so it doesn't even go into the while loop because the 'commandResult.readLine()' really is null so it skips over the loop entirely. I've updated the code... collabedit.com/5r384Foul

© 2022 - 2024 — McMap. All rights reserved.