简体   繁体   中英

Failing to get output when invoking shell command from Java code

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

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();
}

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().

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM