I am using FFMPEG to convert an audio file. I call the command line and in turn FFMPEG from Java. I do this using Runtime and Process Runtime.exec(). For the process I have an InputStream and an ErrorStream. For some reason, even though the FFMPEG commands works fine, it prints out from the Error stream giving the impression it has failed. Anyone know why this might be happening? Obviously it's not a major issue as it does work fine, but if for some reason it does actually error, or someone new to the project does not realise this is how it works it could be confusing.
Any ideas?
Here is the relevant code:
Runtime rt = Runtime.getRuntime();
System.out.println("Execing " + cmd);
Process proc = rt.exec(cmd);
StreamGobbler errorGobbler = new StreamGobbler(proc.getErrorStream(), "ERROR");
// any output?
StreamGobbler outputGobbler = new StreamGobbler(proc.getInputStream(), "OUTPUT");
// kick them off
errorGobbler.start();
outputGobbler.start();
// any error???
int exitVal = proc.waitFor();
System.out.println("ExitValue: " + exitVal);
And the StreamGobbler class:
class StreamGobbler extends Thread {
InputStream is;
String type;
StreamGobbler(InputStream is, String type) {
this.is = is;
this.type = type;
}
public void run() {
try {
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String line = null;
while ((line = br.readLine()) != null)
System.out.println(type + ">" + line);
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
What you are experiencing is the standard practice across all *NIX tools: they send the "business value" data to stdout and separately send any diagnostic output to stderr. This is an important hallmark of tools written in the "small is beautiful" paradigm, which greatly values the concept of piping one tool's output into the next one's input, thereby creating an ad-hoc chain of information processing. The complete scheme would break down if the tool sent "Processing... 50% done" kind of output to stdout.
Feel free to ignore such output on stderr, but make sure you consume it, as you are doing now, in order to avoid blocking due to overfull output buffers.
Related
In windows, use Java, mostly we can call Runtime.getRuntime().exec to execute an executable application or batch file, then call proc.getErrorStream() proc.getInputStream() to get the standard output/error stream.
but in this time, I have an application called 'caption2ass.exe' (caption2ass.exe is a well knownd popular tool that can extract ass subtitle from Transport Stream), it prints a lot of information into the screen, but it seems that Java program CAN NOT receive the information by calling proc.getErrorStream() or proc.getInputStream().
Manually I typed 'caption2ass.exe' in the command line, and then I pressed [enter]. after that, the screen will show:
I am trying to receive The infomation in the screen and put it into sysout, or put it into an string array in future.
My Java code is as below:
main program:
String cmd = "E:\\program_media\\Mikey's Fansub Utilities\\TS-OneKeyProcess\\tools\\caption2ass-pcr\\Caption2Ass_PCR.exe";
Runtime run = Runtime.getRuntime();
Process proc = run.exec(cmd);
StreamGobbler errorGobbler = new StreamGobbler(
proc.getErrorStream(), "GBK", "ERR", System.err);
StreamGobbler outputGobbler = new StreamGobbler(
proc.getInputStream(), "GBK", "OUT", System.out);
errorGobbler.start();
outputGobbler.start();
int exitVal = proc.waitFor();
System.out.println("ExitValue: " + exitVal);
StreamGobbler.java :
public class StreamGobbler extends Thread {
InputStream in;
String charsetName;
String type;
PrintStream out;
StreamGobbler(InputStream inputStream, String charsetName, String type, PrintStream out) {
this.in = inputStream;
this.charsetName = charsetName;
this.type = type;
this.out = out;
}
#Override
public void run() {
try {
InputStreamReader isr = new InputStreamReader(in, charsetName);
char[] cbuf = new char[256];
int len = -1;
while ( -1 != (len=isr.read(cbuf))){
out.print(Arrays.copyOf(cbuf, len));
}
} catch (IOException ioe) {
ioe.printStackTrace();
} finally {
IOUtils.closeQuietly(in);
}
}
}
After running this java program, I only got a strange character before ExitValue:
So, My question is: How to get the output information in the screen of this 'caption2ass.exe' using java?
you can get caption2ass from here : http://pan.baidu.com/s/1nuCClXR
, you can run this program in your sandbox if you dare not run an unknown program, especially from Baidu.
Any tests are welcome.
谢谢各位的回复。
I have found the source code of caption2ass, it is a c++ program.
I have changed every logging statment, let them log information into stdout, and then recompiled it.
so i can easily receive the output of caption2ass in the java program, using proc.getInputStream() now.
I am using correct practices mentioned in this article:
http://www.javaworld.com/article/2071275/core-java/when-runtime-exec---won-t.html?page=2
I read the Output and Error streams in concurrently. However, when reading the Output stream of the process, it hangs on readLine() after the last line. I have no idea how to work around this. It is the line of the process and it just hangs there.
The command that hangs this is paexec \192.168.1.92 -c -f C:\Windows\ITBBsync0.bat.
Inside the batch file there are several line such as the following: devcon.exe status =USB > C:\Windows\output.txt
When I execute it on command line, process exits with code 0. When I execute it in Java it hangs after reading the last line of output (which is basically the last line of the batch file). I believe the process is not exiting which is why the issue occurs.
import java.io.*;
class StreamGobbler extends Thread
{
InputStream is;
String type;
StreamGobbler(InputStream is, String type)
{
this.is = is;
this.type = type;
}
public void run()
{
try
{
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String line=null;
while ((line = br.readLine()) != null){
System.out.println(type + ">" + line);
//hangs after reading last line.
}
} catch (IOException ioe)
{
ioe.printStackTrace();
}
}
}
Also the method that uses the StreamGobbler is the following. There is obviously a parent class which executes this method.
public static boolean cmd(String command){
try{
Runtime rt = Runtime.getRuntime();
System.out.println("Execing " + command);
Process p = rt.exec(command);
// any error message?
StreamGobbler errorGobbler = new StreamGobbler(p.getErrorStream(),"ERROR");
StreamGobbler outputGobbler = new StreamGobbler(p.getInputStream(), "OUTPUT");
errorGobbler.start();
outputGobbler.start();
int exitVal = p.waitFor();
if (exitVal != 0){
System.out.println("ExitValue: " + exitVal);
System.out.println(command + "EXIT CODE:" + exitVal);
return false;
}
return true;
}catch(IOException ioe){
ioe.printStackTrace();
return false;
}catch(InterruptedException ie){
ie.printStackTrace();
return false;
}
}
I have visited many threads on StackOverflow and as far as I know I am using best practices for this. Please let me know how I can fix this or why this may be happening.
I can't give you exactly the application I am executing because it is a bit complex, but the application performs some network operations and also executes a batch file which in turns contains more operations.
I'm launching a process in the following way.
try {
final Process mvnProcess = new ProcessBuilder("cmd", "/c", "mvn", "--version")
.directory(new File(System.getProperty("user.dir")))
.inheritIO()
.start();
System.exit(mvnProcess.waitFor());
} catch (final IOException ex) {
System.err.format(IO_EXCEPTION);
System.exit(1);
} catch (final InterruptedException ex) {
System.err.format(INTERRUPTED_EXCEPTION);
System.exit(1);
}
Since I invoke inheritIO() I was expecting the sub-process's output on the console, but nothing appears. What am I missing here?
Edit: I know that I can use mvnProcess.getInputStream() and read the process's output explicitly, writing it to the console (or where-ever) in a loop. I don't like this solution however, since the loop will block my thread. inheritIO() looked promising, but apparently I don't understand how it works. I was hoping someone here could shed some light on this.
Maybe it is an option the read it from the subprocess:
Add this code after start() and you will have it printed to stdout:
InputStream is = mvnProcess.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String line;
while ((line = br.readLine()) != null)
{
System.out.println(line);
}
You can use the .redirectError(Redirect.INHERIT).
It sets the source and destination for sub process standard I/O to be the same as those of the current Java process.
I'm trying to find a solution how to implement a multiple command - response interaction with the Windows cmd shell. Example:
Start the cmd shell
"dir"
wait for and Handle input
Execute new command depending on the input content
wait for and Handle input
etc.
PLEASE NOTE! Steps above were only to describe the way of communication, it is NOT my intention to browse the file system, i.e. the actual commands could be something else.
Approach so far:
try {
Runtime rt = Runtime.getRuntime();
p = rt.exec("cmd");
error = p.getErrorStream();
input = p.getInputStream();
output = new PrintStream(p.getOutputStream());
StreamGobbler errGobbler = new StreamGobbler(error, "ERROR");
StreamGobbler inGobbler = new StreamGobbler(input, "INPUT");
errGobbler.start();
inGobbler.start();
output.println("dir");
output.flush();
sleep(5);
output.println("dir");
output.flush();
} catch (IOException e) {
System.out.println(e.printStackTrace());
}
StreamGobbler class:
class StreamGobbler extends Thread
{
InputStream is;
String type;
ArrayList<String> cmdRespArr = new ArrayList<String>();
StreamGobbler(InputStream is, String type) {
this.is = is;
this.type = type;
}
public void run() {
try {
InputStreamReader isr = new InputStreamReader(is);
BufferedReader bf = new BufferedReader(isr);
String line = null;
while ( ( line = bf.readLine() ) != null ) {
cmdRespArr.add(line);
}
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
}
In this example however the while loop in the run method never returns between the issued commands (this is not part of the problem).
So, would the approach be to let the while method add the line read to a collection or other container, and then monitor that one for something indicating that the cmd shell is ready for input (which would in turn indicate that all available input from previous command have been read). And then fire off the next command?
In the example above this indication would get rid off the sleep call which right now is only there for debugging purposes.
I have a vague memory that this was the approach when doing it with Python.
Or is this totally wrong?
Will it be a solution to start multiple command processors, i.e. one per command?
I'm asking because with keeping one command processor open, it is very hard to determine when a command has been processed, unless you parse the output line by line and wait until you see the prompt in the output.
With multiple processors, i.e. executing "cmd /c dir" then input output redirs will close when the command has completed (and the associated process terminated).
Of course this will not work, if some commands depend on others, e.g. doing a chdir and expecting the next command to work in that dir.
In a java program, I am generating an sh script for use on a centOS machine, which will use sox and lame to decode an MP3 audio file, then apply some gain to the file respectively. Im having some issues getting the Process.waitFor() method to do anything other than hang indefinitely. Here is the code:
try
{
// TODO code application logic here
String reviewPath = "/SomeDirectory/";
String fileName = "FileName";
String extension = ".mp3";
StringBuilder sb = new StringBuilder();
sb.append("#!/bin/bash\n");
sb.append("cd " + reviewPath + "\n");
sb.append("lame --decode " + fileName + extension + "\n");
File script = new File(reviewPath + fileName + ".sh");
script.createNewFile();
script.setExecutable(true);
FileWriter writer = new FileWriter(script);
writer.write(sb.toString());
writer.close();
Process p = Runtime.getRuntime().exec(script.getAbsolutePath());
String line;
BufferedReader bri = new BufferedReader
(new InputStreamReader(p.getInputStream()));
BufferedReader bre = new BufferedReader
(new InputStreamReader(p.getErrorStream()));
while ((line = bri.readLine()) != null) {
System.out.println(line);
}
bri.close();
while ((line = bre.readLine()) != null) {
System.out.println(line);
}
bre.close();
p.waitFor();
System.out.println("Done.");
}
catch (Exception e)
{
System.out.println(e.getMessage());
}
The odd part is that when I run the .sh file it generates by hand, it runs and exits nicely, but when I execute it from a process object in java, it never exits. The exitValue of the process is always "Process has not exited". Ive tried adding set -e to the script, and exit to the end of the script. Short of using the kill command (which I dont really think I can do here) Im at a loss as to what is going on here. Any suggestions?
Add something like while(p.getInputStream().read() != -1); after starting the process. The buffer will get filled and the process will stop waiting for something (in this case, your program) to read from it to free up space.
I figured it out! The problem here was indeed that the output streams needed to be flushed for the application to exit, but simply reading from the streams is not enough. I used Suresh Koya's suggestion and used the processBuilder api, and redirected the error stream on the process before starting it, and read from the streams. This fixed the issues I was having :D