1

I am trying to invoke a shell command through Java code following the instructions of Mkyong. My code is :

public class ExecuteShellCommand {

    public static void main(){
        String absolutePath = "/home/marievi/Downloads/small.mp4";
        String command = "ffmpeg -i " + absolutePath;
        ExecuteShellCommand obj = new ExecuteShellCommand();
        String output = obj.executeCommand(command);
        System.out.println(output);
    }

    public String executeCommand(String command) {

        StringBuffer output = new StringBuffer();

        Process p;
        try {
            p = Runtime.getRuntime().exec(command);
            p.waitFor();
            BufferedReader reader =
                    new BufferedReader(new InputStreamReader(p.getInputStream()));

            String line = "";
            while ((line = reader.readLine()) != null) {
                output.append(line + "\n");
            }

        } catch (Exception e) {
            e.printStackTrace();
        }

        return output.toString();
    }
}

but I get an empty output, and only a newline is printed. However, when I run the code with Mkyong's example :

public static void main(){
    ExecuteShellCommand obj = new ExecuteShellCommand();
    String domainName = "google.com";
    String command = "ping -c 3 " + domainName;
    String output = obj.executeCommand(command);
    System.out.println(output);
}

output is printed. Any idea what is going wrong? When I directly execute the command :

ffmpeg -i /home/marievi/Downloads/small.mp4

from the command line, I get the desired output :

ffmpeg version 3.3.4-1~14.04.york1 Copyright (c) 2000-2017 the FFmpeg developers
  built with gcc 4.8 (Ubuntu 4.8.4-2ubuntu1~14.04.3)
  configuration: --prefix=/usr --extra-version='1~14.04.york1' --toolchain=hardened --libdir=/usr/lib/x86_64-linux-gnu --incdir=/usr/include/x86_64-linux-gnu --enable-gpl --disable-stripping --enable-avresample --enable-avisynth --enable-gnutls --enable-ladspa --enable-libass --enable-libbluray --enable-libbs2b --enable-libcaca --enable-libcdio --enable-libflite --enable-libfontconfig --enable-libfreetype --enable-libfribidi --enable-libgme --enable-libgsm --enable-libmp3lame --enable-libopenjpeg --enable-libmodplug --enable-libopus --enable-libpulse --enable-librubberband --enable-libshine --enable-libsnappy --enable-libsoxr --enable-libspeex --enable-libssh --enable-libtheora --enable-libtwolame --enable-libvorbis --enable-libvpx --enable-libwavpack --enable-libwebp --enable-libx265 --enable-libxvid --enable-libzmq --enable-libzvbi --enable-omx --enable-openal --enable-opengl --enable-sdl2 --enable-libdc1394 --enable-libiec61883 --enable-chromaprint --enable-frei0r --enable-libopencv --enable-libx264 --enable-shared
  libavutil      55. 58.100 / 55. 58.100
  libavcodec     57. 89.100 / 57. 89.100
  libavformat    57. 71.100 / 57. 71.100
  libavdevice    57.  6.100 / 57.  6.100
  libavfilter     6. 82.100 /  6. 82.100
  libavresample   3.  5.  0 /  3.  5.  0
  libswscale      4.  6.100 /  4.  6.100
  libswresample   2.  7.100 /  2.  7.100
  libpostproc    54.  5.100 / 54.  5.100
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from '/home/maxez/Downloads/small.mp4':
  Metadata:
    major_brand     : mp42
    minor_version   : 0
    compatible_brands: mp42isomavc1
    creation_time   : 2010-03-20T21:29:11.000000Z
    encoder         : HandBrake 0.9.4 2009112300
  Duration: 00:00:05.57, start: 0.000000, bitrate: 551 kb/s
    Stream #0:0(und): Video: h264 (Constrained Baseline) (avc1 / 0x31637661), yuv420p(tv, bt709), 560x320, 465 kb/s, 30 fps, 30 tbr, 90k tbn, 60 tbc (default)
    Metadata:
      creation_time   : 2010-03-20T21:29:11.000000Z
      encoder         : JVT/AVC Coding
    Stream #0:1(eng): Audio: aac (LC) (mp4a / 0x6134706D), 48000 Hz, mono, fltp, 83 kb/s (default)
    Metadata:
      creation_time   : 2010-03-20T21:29:11.000000Z
4
  • Are you missing a slash in your absolutePath? Because home/marievi/... is not an absolute path. Should be /home/marievi/... (look at the declaration of variable absolutePath). Commented Sep 25, 2017 at 13:03
  • @Jesper I don't miss the /, it was a typo, edited, sorry. Commented Sep 25, 2017 at 13:04
  • I assume this is a typo as well but just want to confirm to make sure it's not your actual problem. In your Java code the path is actually different from the path in the direct command you give. IE you are using /marievi as opposed to /maxez. Commented Sep 25, 2017 at 13:07
  • @MichaelPlatt sorry, also a typo! Commented Sep 25, 2017 at 13:08

2 Answers 2

4

Do yourself a favor and avoid relying on mkyong.com. That site just regurgitates things you can easily find in public documentation, with no regard to how bad or stale its advice is, or how obsolete the information is.

Indeed, Runtime.exec has been obsolete for the last thirteen years. Its replacement, introduced way back in Java 5, is ProcessBuilder. By using ProcessBuilder, you can allow the error output of the external process to be shown in the same place as your Java program’s error output. Currently, you are not showing it anywhere, so there’s no way to know what went wrong.

Similarly, StringBuffer is obsolete and very old; its replacement is StringBuilder. They are identical except that StringBuffer has the extra overhead of thread safety, which is rarely useful.

Also, waitFor() waits for the process to end. Obviously, then, you should not be calling it before you have read the output of the process. Call it after you have read all lines of output.

Finally, an exception means “the operation did not succeed, and you should not continue as if it did succeed.” If you get an exception, your process did not succeed. Since reading the output of the process is fundamental to what you’re trying to do, it does not make sense to continue. Instead, wrap any caught exception in an unchecked exception, like RuntimeException. (An even better choice would be removing the try/catch entirely, and adding those exception types to the throws clause your executeCommand and main methods.)

public static void main() {
    String absolutePath = "/home/marievi/Downloads/small.mp4";
    String[] command = { "ffmpeg", "-i", absolutePath };
    ExecuteShellCommand obj = new ExecuteShellCommand();
    String output = obj.executeCommand(command);
    System.out.println(output);
}

public String executeCommand(String[] command) {

    StringBuilder output = new StringBuilder();

    try {
        ProcessBuilder builder = new ProcessBuilder(command);
        // Share standard input/output/error descriptors with Java process...
        builder.inheritIO();
        // ... except standard output, so we can read it with getInputStream().
        builder.redirectOutput(ProcessBuilder.Redirect.PIPE);

        Process p = builder.start();

        try (BufferedReader reader =
            new BufferedReader(new InputStreamReader(p.getInputStream()))) {

            String line = "";
            while ((line = reader.readLine()) != null) {
                output.append(line + "\n");
            }
        }

        p.waitFor();

    } catch (IOException | InterruptedException e) {
        // Process failed;  do not attempt to continue!
        throw new RuntimeException(e);
    }

    return output.toString();
}
Sign up to request clarification or add additional context in comments.

1 Comment

Despite the asker didn't call for all these details - this indeed is a far more useful answer.
1

There is nothing wrong with your code. Only ffmpeg writes to stderr instead of stdout.

You can make it work by replacing getInputStream() with getErrorStream().

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.