java processbuilder ffmpeg pipe - java

i try to run ffmpeg out java. here my code:
String[] temp = {"ffmpeg\\ffmpeg.exe","-i","input_track.ac3","-threads","0","-af","volume=volume="0.0"dB","-acodec","pcm_s32le","-ac","6","-ar","48000","-f","wav","-","|","ffmpeg\\fdkaac","--ignorelength","-m","1","-o","ouput_track.aac","-"};
ProcessBuilder pb = new ProcessBuilder(temp);
Process p = pb.start();
int ev = 0;
if (p.waitFor() != 0)
{
ev = p.exitValue();
}
i try the comand at windows cmd, here have a problem with "|" at the ffmpeg command line.
maybe someone say my fould?
best regards

This question is similar to How to make pipes work with Runtime.exec()? ... except that it is for Windows.
The problem is essentially the same: the exec methods don't understand shell syntax such as pipes, input or output direction and so on. The solution is essentially the same too: exec the appropriate shell and get that to handle the shell syntax.
In this case, try something like this:
String[] temp = new String[] {
"cmd", "/c",
"ffmpeg\\ffmpeg.exe -i input_track.ac3 -threads 0 " +
"-af volume=volume=\"0.0\"dB -acodec pcm_s32le -ac 6 " +
"-ar 48000 -f wav - | " +
"ffmpeg\\fdkaac --ignorelength -m 1 -o ouput_track.aac -"
};
Note that the actual command is a single string. (The quotes around the 0.0 look a bit strange, but that is what you have in your question.)

| is a shell pipe character, in java you'll have to either run this command in a shall (bash -c "the whole commandline | goes here"), or you'll have to run two processes (the one before the | and the one after), where the stdout of the first writes into the stdin of the second. For this, you'd typically use redirectOutput(Redirect.PIPE) and redirectInput(Redirect.PIPE).

Related

Bash command not working with ProcessBuilder

The following command executes fine in bash:
Command:
bash -c "$(echo 'H4sIAArQ/mAAA1WMuw7CIBRAd77ihLJqtKuTg19hHIjetiQU0svl/1sn43weaeKJD4PnlI2R1w1bpOBA3kvF340ssX1Z1LmvUqyhsvWk8jl7nOQmP/2x9ZixSlXWqnLcYvlrw4VwJYxHOiW3AwCHgS2AAAAA' | base64 --decode | zcat)" - -a -b
Output:
Equal to or more than 2 arguments - -a -b
Wanted to know - how can I achieve this using Java's ProcessBuilder?
I tried the following:
ProcessBuilder processBuilder = new ProcessBuilder(args);
where args are:
bash
-c
"$(echo 'H4sIAArQ/mAAA1WMuw7CIBRAd77ihLJqtKuTg19hHIjetiQU0svl/1sn43weaeKJD4PnlI2R1w1bpOBA3kvF340ssX1Z1LmvUqyhsvWk8jl7nOQmP/2x9ZixSlXWqnLcYvlrw4VwJYxHOiW3AwCHgS2AAAAA' | base64 --decode | zcat)"
-
-a
-b
But I keep on getting the following error:
-: if: command not found
Process finished with exit code 127
Can someone please point out the issue here?
Command substitution results, in bash, don't go through all parsing steps. That means that compound commands like if aren't honored, command separators like ; have no syntactic meaning, etc.
If you want to override that and force an additional parsing pass, you need to use eval. Thus:
args = String[]{
"bash",
"-c",
"eval \"$(echo 'H4sIAArQ/mAAA1WMuw7CIBRAd77ihLJqtKuTg19hHIjetiQU0svl/1sn43weaeKJD4PnlI2R1w1bpOBA3kvF340ssX1Z1LmvUqyhsvWk8jl7nOQmP/2x9ZixSlXWqnLcYvlrw4VwJYxHOiW3AwCHgS2AAAAA' | base64 --decode | zcat)\"",
"-",
"-a",
"-b",
}
Why did this work when you ran it in a shell, instead of from a ProcessBuilder? Because that shell you ran it in would perform the command substitution in "$(...)", and put the results of that substitution in the text it passed to the child shell; so the substitution was already done at parsing time.

Using Java's Runtime.getRuntime().exec I get error with some commands, success with others -- how can this be determined? [duplicate]

When I try to run Runtime.exec(String), certain commands work, while other commands are executed but fail or do different things than in my terminal. Here is a self-contained test case that demonstrates the effect:
public class ExecTest {
static void exec(String cmd) throws Exception {
Process p = Runtime.getRuntime().exec(cmd);
int i;
while( (i=p.getInputStream().read()) != -1) {
System.out.write(i);
}
while( (i=p.getErrorStream().read()) != -1) {
System.err.write(i);
}
}
public static void main(String[] args) throws Exception {
System.out.print("Runtime.exec: ");
String cmd = new java.util.Scanner(System.in).nextLine();
exec(cmd);
}
}
The example works great if I replace the command with echo hello world, but for other commands -- especially those involving filenames with spaces like here -- I get errors even though the command is clearly being executed:
myshell$ javac ExecTest.java && java ExecTest
Runtime.exec: ls -l 'My File.txt'
ls: cannot access 'My: No such file or directory
ls: cannot access File.txt': No such file or directory
meanwhile, copy-pasting to my shell:
myshell$ ls -l 'My File.txt'
-rw-r--r-- 1 me me 4 Aug 2 11:44 My File.txt
Why is there a difference? When does it work and when does it fail? How do I make it work for all commands?
Why do some commands fail?
This happens because the command passed to Runtime.exec(String) is not executed in a shell. The shell performs a lot of common support services for programs, and when the shell is not around to do them, the command will fail.
When do commands fail?
A command will fail whenever it depends on a shell features. The shell does a lot of common, useful things we don't normally think about:
The shell splits correctly on quotes and spaces
This makes sure the filename in "My File.txt" remains a single argument.
Runtime.exec(String) naively splits on spaces and would pass this as two separate filenames. This obviously fails.
The shell expands globs/wildcards
When you run ls *.doc, the shell rewrites it into ls letter.doc notes.doc.
Runtime.exec(String) doesn't, it just passes them as arguments.
ls has no idea what * is, so the command fails.
The shell manages pipes and redirections.
When you run ls mydir > output.txt, the shell opens "output.txt" for command output and removes it from the command line, giving ls mydir.
Runtime.exec(String) doesn't. It just passes them as arguments.
ls has no idea what > means, so the command fails.
The shell expands variables and commands
When you run ls "$HOME" or ls "$(pwd)", the shell rewrites it into ls /home/myuser.
Runtime.exec(String) doesn't, it just passes them as arguments.
ls has no idea what $ means, so the command fails.
What can you do instead?
There are two ways to execute arbitrarily complex commands:
Simple and sloppy: delegate to a shell.
You can just use Runtime.exec(String[]) (note the array parameter) and pass your command directly to a shell that can do all the heavy lifting:
// Simple, sloppy fix. May have security and robustness implications
String myFile = "some filename.txt";
String myCommand = "cp -R '" + myFile + "' $HOME 2> errorlog";
Runtime.getRuntime().exec(new String[] { "bash", "-c", myCommand });
Secure and robust: take on the responsibilities of the shell.
This is not a fix that can be mechanically applied, but requires an understanding the Unix execution model, what shells do, and how you can do the same. However, you can get a solid, secure and robust solution by taking the shell out of the picture. This is facilitated by ProcessBuilder.
The command from the previous example that requires someone to handle 1. quotes, 2. variables, and 3. redirections, can be written as:
String myFile = "some filename.txt";
ProcessBuilder builder = new ProcessBuilder(
"cp", "-R", myFile, // We handle word splitting
System.getenv("HOME")); // We handle variables
builder.redirectError( // We set up redirections
ProcessBuilder.Redirect.to(new File("errorlog")));
builder.start();

Defunct processes when java start terminal execution

I try to ocr some images with kraken. I prepared a console command for doing that.
It was slow, so I combined that with gnu parallel.
find temp/ -name '*.tif' -or -name '*.jpg' | parallel -j4 kraken -i {} {}.html binarize segment ocr -h
It works fine, when I'm doing this in the terminal. When I start this in java(eclipse), the execution stops after 30 images. It does not terminate. It left defunct processes.
String command = "find temp/ -name '*.tif' -or -name '*.jpg' | parallel -j4 kraken -i {} {}.html binarize segment ocr -h";
Process p = Runtime.getRuntime().exec(new String[]{"/bin/bash","-c",command});
p.waitFor() == 0;
I tried several configurations(more memory(eclipse and the exceution), less threads), but nothing helped.
Has someone an idea to avoid defunct processes or how the execution can be started again?
Almost certainly, the problem is that you're not consuming the output of the process, causing its output buffer to fill and therefore the process to stall.
Try:
String command = "find temp/ -name '*.tif' -or -name '*.jpg' | parallel -j4 kraken -i {} {}.html binarize segment ocr -h";
Process p = Runtime.getRuntime().exec(new String[]{"/bin/bash","-c",command});
InputStream is = p.getInputStream();
// is.skip(Long.MAX_VALUE); Doesn't work
while (is.read() != -1) { } // consume all process output
p.waitFor();
A complete solution would also process the error stream. This can be done by starting a separate thread which reads/skips the input from the error stream.
(Alternatively, you could redirect output to /dev/null in the bash command script).

Java execute debian terminal command with "|"

I am experiencing a really confusing issue about sending commands to terminal via Java.
I have exactly this code:
Process p = Runtime.getRuntime().exec(new String[]{"useradd", server, "-p", pass, "-d", "/home/dakadocp/servers/" + server, "-s", "/bin/false"});
Runtime.getRuntime().exec(new String[]{"echo", server + ":" + pass, "|", "chpasswd"});
The first command is this one "useradd user -p password -d /home/ftp/test/ -s /bin/false" and the second one should be this echo username:new_password | chpasswd, the first command works without any problem and creates the user which I define by the "server" variable, but when I try to execute the second command to change users pass this command probably never happen, output is null and the password is not changed, but when I type this command directly to the terminal it works perfectly so this is just Java issue.
I think the problem is in the character "|", before I tried also some command with "|" and its behavior was the same as with this command. What I am doing wrongly ?
Thanks.
Welite.
| is a shell feature, and requires a shell to run. The trivial fix is to run a shell:
Runtime.getRuntime().exec(new String[] { "sh", "-c", "echo something | chpasswd" });
However, java is more than capable of writing to a process with needing a shell or echo. The better way is to just run chpasswd by itself and writing the string to it:
Process p = Runtime.getRuntime().exec(new String[] { "chpasswd" });
PrintWriter writer = new PrintWriter(p.getOutputStream());
writer.println("foo:bar");
writer.close();

shell script if statement executed from java?

I'm trying to execute unix commands thru a java program. Some of these commands involve an if-then-fi statement. Can this be done thru java / Runtime class? Seems like it only handles 1 command at a time.
I'm looking to do something like this:
grep 'Error One' SystemErr.log > $HOME/tempFiles/output.txt
grep 'Error Two' SystemErr.log >> $HOME/tempFiles/output.txt
grep 'Error Three' SystemErr.log >> $HOME/tempFiles/output.txt
.
.
if [ -s $HOME/tempFiles/output.txt ]
then
mail -s "Subject here" "a#b.com" < $HOME/tempFiles/output.txt
fi
Basically, I just want to email the file (results) if the grep found anything.
I want to use java instead of a direct shell script so that the errors I search for can be database-driven, easier to change.
I know I could read the file myself in java and search/parse it myself. But grep and other unix commands have a lot of built-in functionality I want to use to make it easier.
Any ideas, or am I totally on the wrong track?
Here is some code, using simpler commands, but basically equivalent:
public static void main( String[] args ) throws Exception {
try {
ProcessBuilder pb = new ProcessBuilder( "/bin/bash", "-c",
"echo one >/tmp/xxx && echo two >>/tmp/xxx && " +
"if [ -s /tmp/xxx ]; then cp /tmp/xxx /tmp/yyy; fi" );
File log = new File( "/tmp/log.txt" );
pb.redirectOutput(ProcessBuilder.Redirect.appendTo(log));
Process process = pb.start();
process.waitFor();
} catch( Exception e ){
// ...
} catch( Error e ){
// ...
}
}
The trick is to put it all into a single shell command so that you can call /bin/bash with the -c command option.
If composing this command is too complicated, write a shell file and source that.

Categories