I am using ProcessBuilderto build my command. I want to build my command following this post:How do I launch a java process that has the standard bash shell environment?
Namely, my command is something like this:
/bin/bash -l -c "my program"
However, I am having difficulties to pass the double quotes into ProcessBuilder, as new ProcessBuilder(List<String> command) failed to phrase the command if I natively add double quotes to List<String> command. ProcessBuilder recognizes the double quotes as an argument.
Relevant code:
//Construct the argument
csi.add("/bin/bash");
csi.add("-l");
csi.add("-c");
csi.add("\"");
csi.add(csi_path);
csi.add(pre_hash);
csi.add(post_hash);
csi.add("\"");
String csi_output = Command.runCommand(project_directory, csi);
public static String runCommand(String directory, List<String> command) {
ProcessBuilder processBuilder = new ProcessBuilder(command).directory(new File(directory));
Process process;
String output = null;
try {
process = processBuilder.start();
//Pause the current thread until the process is done
process.waitFor();
//When the process does not exit properly
if (process.exitValue() != 0) {
//Error
System.out.println("command exited in error: " + process.exitValue());
//Handle the error
return readOutput(process);
}else {
output = readOutput(process);
System.out.println(output);
}
} catch (InterruptedException e) {
System.out.println("Something wrong with command: " +e.getMessage());
} catch (IOException e) {
System.out.println("Something wrong with command: " +e.getMessage());
}
return output;
}
Ps: I do want to use ProcessBuilder instead of Runtime.getRuntime.exec() because I need to run the command in a specific directory. I need to use ProcessBuilder.directory().
Ps: The command will exit with 2 after running. It seems that the system can recognize this command. The strange thing is that it has no output after exiting with 2.
Ps: The expected command is /bin/bash -l -c "/Users/ryouyasachi/GettyGradle/build/idea-sandbox/plugins/Getty/classes/python/csi 19f4281 a562db1". I printed the value and it was correct.
Best way to troubleshoot your problem is to construct the command first and pass it to the list. So, instead of doing all this.
csi.add("/bin/bash");
csi.add("-l");
csi.add("-c");
csi.add("\"");
csi.add(csi_path);
csi.add(pre_hash);
csi.add(post_hash);
csi.add("\"");
You should first construct the command
StringBuilder sb = new StringBuilder();
sb.append("/bin/bash -l -c");
sb.append("\""+csi_path+pre_hash+post_hash+"\"");// add whitespace between the varaible, if required.
System.outprintln(sb.toString()); //verify your command here
csi.add(sb.toString());
Also, verify all above variable values.
Thx for #Ravi 's idea!
//Construct the argument
csi.add("/bin/bash");
csi.add("-l");
csi.add("-c");
csi.add("\"" + csi_path + " " + pre_hash+ " " + post_hash + "\"");
String csi_output = Command.runCommand(project_directory, csi);
The Process has to take each argument separately in order to recognize the command. The tricky part is that, in my desired command
/bin/bash -l -c "/mypath/csi"
"/mypath/csi" needs to be viewed as one single argument by Process.
Related
if (args.length == 0&&runningFromIntelliJ()==false) {
String OS = System.getProperty("os.name", "generic").toLowerCase(Locale.ENGLISH);
if (OS.indexOf("win") >= 0) {
String path = CODE.run.class.getProtectionDomain().getCodeSource().getLocation().getPath().substring(1);
String decodedPath = null;
try {
decodedPath = URLDecoder.decode(path, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
try {
Runtime.getRuntime().exec("cmd"+" /c start java -jar \"" + decodedPath + "\" run");
} catch (IOException e) {
e.printStackTrace();
}
System.exit(0);
}
}
}
This Code is Starting a programm in cmd after i double clicked it. the problem is that it only works in windows And I want to run it on my raspberry pi. The problem now is that I habe no Idea how a can start a .jar with args in xterm.
runningFromIntelliJ() is just testing if I am running the programm in IntelliJ and skips that part if I do.
It seems like your app will do nothing once there's an argument passed to the jar.
if(args.length==0)
will be false hence the whole if. If you have other code to run with Arguments, try this:
java -jar yourJar.jar "arg 1" arg2#
Otherwise, review your code.
Here is one way to start a program with xterm.
xterm -hold -e '/bin/bash -c "ls -l"'
In this example, the program is simply the ls command, but it should be self-explanatory how to use it for java; just use the example found in another answer.
In your java code, what that looks like is:
Runtime.getRuntime().exec(String.format("xterm -hold -e '/bin/bash -c \"%s\"'", "java -jar '" + decodedPath + "' run"));
or without a shell:
Runtime.getRuntime().exec(String.format("xterm -hold -e 'java -jar \"%s\" run'", decodedPath));
I have problems executing this ffmpeg command in my java code:
ffmpeg -i sample.mp4 -i ad.mp4 -filter_complex "[0:v]trim=0:15,setpts=PTS-STARTPTS[v0]; [1:v]trim=0:5,setpts=PTS-STARTPTS[v1]; [0:v]trim=20:30,setpts=PTS-STARTPTS[v2]; [v0][v1][v2]concat=n=3:v=1:a=0[out]" -map "[out]" output.mp4
I used the getRuntime() method below, but that doesn't work for me. Even if I simply remove the ", still it doesn't work. When I simply copy-paste the equivalent string in terminal, it works.
String c1=" -i "+dir+"sample.mp4 "+"-i "+dir+"ad.mp4 -filter_complex [0:v]trim=0:15,setpts=PTS-STARTPTS[v0]; [1:v]trim=0:5,setpts=PTS-STARTPTS[v1]; [0:v]trim=20:30,setpts=PTS-STARTPTS[v2]; [v0][v1][v2]concat=n=3:v=1:a=0[out] -map [out] "+dir+"output.mp4";
RunCommand("ffmpeg"+c1);
Using this method:
private static void RunCommand(String command) throws InterruptedException {
try {
// Execute command
Process proc = Runtime.getRuntime().exec(command);
System.out.println(proc.exitValue());
// Get output stream to write from it
// Read the output
BufferedReader reader =
new BufferedReader(new InputStreamReader(proc.getInputStream()));
String line = "";
while((line = reader.readLine()) != null) {
System.out.print(line + "\n");
// System.out.println(ads.get(0));
}
proc.waitFor();
} catch (IOException e) {
}
}
This one doesn't work also, and printing the exit value shows this:
Exception in thread "main" java.lang.IllegalThreadStateException: process hasn't exited
at java.lang.UNIXProcess.exitValue(UNIXProcess.java:423)
at parser.Parser.RunCommand(Parser.java:106)
at parser.Parser.commandGenerator2(Parser.java:79)
at parser.Parser.main(Parser.java:44)
If I move the proc.waitFor(); before printing the exit value, it is 1.
What is the problem? Why it doesn't run in Java code?
There is some issue on your code, First, use thread to stream in and err of inner process to the console
Create a pipe stream class like :
class PipeStream extends Thread {
InputStream is;
OutputStream os;
public PipeStream(InputStream is, OutputStream os) {
this.is = is;
this.os = os;
}
public void run() {
byte[] buffer=new byte[1024];
int len;
try {
while ((len=is.read(buffer))>=0){
os.write(buffer,0,len);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
Then adapt the runtime part to:
System.out.println("Launching command: "+command);
ProcessBuilder pb = new ProcessBuilder("/bin/sh", "-c", command);
Process proc=pb.start();
PipeStream out=new PipeStream(proc.getInputStream(), System.out);
PipeStream err=new PipeStream(proc.getErrorStream(), System.err);
out.start();
err.start();
proc.waitFor();
System.out.println("Exit value is: "+proc.exitValue());
It will show the command that will be run, the logs and potentially the error.
You will be able to copy paste the command to check on a terminal what is going on if needed.
EDIT: This is very funny. You code was missing some escape char AND there is not visible char in your code. I saw them when I copy paste the line of code. Copy paste the following line in your code, it will remove the error :
String command="ffmpeg -i "+dir+"sample.mp4 -i "+dir+"ad.mp4 -filter_complex '[0:v]trim=0:15,setpts=PTS-STARTPTS[v0]; [1:v]trim=0:5,setpts=PTS-STARTPTS[v1]; [0:v]trim=20:30,setpts=PTS-STARTPTS[v2]; [v0][v1][v2]concat=n=3:v=1:a=0[out]' -map '[out]' "+dir+"output.mp4";
Several issues to fix (most are addressed by other answers individually, with various degrees of explanation, but you need to fix all):
you need to pass arguments to ffmpeg exactly as the shell would, that means you either need to build a command as string array with individual arguments, or, to make it easier (and identical to shell behavior), quote your arguments: add pair of \" around the big filter argument. Then you should be able to pass command to runCommand exactly as you write it in a shell.
but java cannot parse those quotes(*) and isolate arguments that will be passed to ffmpeg, but /bin/sh can do that for you: wrap your command with /bin/sh -c ... (for that I will use ProcessBuilder below)
you need to consume output, or your process might block eternally. ProcessBuilder to the rescue: redirect stderr to stdout to only get a single stream to consume, then redirect stdout wherever you want (below I inherit from parent process, so it goes to the output of your java process itself.
you need to wait for process to complete before getting its exit value (below I use waitFor(), which waits as long as necessary, but there are other options)
[Added after #wargre caught that] not to steal, but for completeness sake you need to make sure the command is not infested with invisible characters ;) For example you were actually passing -fi......lter_complex (dots in hex are e2 80 8c e2 80 8b), but there are more scattered in your command.
Thus:
String c1 = " -i " + dir + "sample.mp4 -i " + dir
+ "ad.mp4 -filter_complex \"[0:v]trim=0:15,setpts=PTS-STARTPTS[v0]; [1:v]trim=0:5,setpts=PTS-STARTPTS[v1]; [0:v]trim=20:30,setpts=PTS-STARTPTS[v2]; [v0][v1][v2]concat=n=3:v=1:a=0[out]\" -map \"[out]\" "
+ dir + "output.mp4";
// Notice additional quotes around filter & map above
runCommand("ffmpeg" + c1);
// ...
static void runCommand(String command) throws IOException, InterruptedException {
Process p = new ProcessBuilder("/bin/sh", "-c", command)
.redirectErrorStream(true)
.redirectOutput(ProcessBuilder.Redirect.INHERIT)
.start();
p.waitFor();
System.out.println("Exit value: " + p.exitValue());
}
(*) In a shell, if you want to print a b you do echo "a b", but in java this does not work:
Runtime.getRuntime().exec("echo \"a b\"");
What it does is naively split around the spaces, and it will pass 2 arguments instead of 1 to echo: "a then b". Not what you want.
Alternative: pass arguments individually.
runCommandAsVarargs(
"ffmpeg",
"-i",
dir + "sample.mp4",
"-i",
dir + "ad.mp4",
"-filter_complex",
"[0:v]trim=0:15,setpts=PTS-STARTPTS[v0]; [1:v]trim=0:5,setpts=PTS-STARTPTS[v1]; [0:v]trim=20:30,setpts=PTS-STARTPTS[v2]; [v0][v1][v2]concat=n=3:v=1:a=0[out]",
"-map",
"[out]",
dir + "output.mp4"
);
// ...
static void runCommandAsVarargs(String... command) throws IOException, InterruptedException {
Process p = new ProcessBuilder(command)
.redirectErrorStream(true)
.redirectOutput(ProcessBuilder.Redirect.INHERIT)
.start();
p.waitFor();
System.out.println("Exit value: " + p.exitValue());
}
It seems like you missed quotes around argument for -filter_complex parameter. Java will run something like this:
ffmpeg -i ./sample.mp4 -i ./ad.mp4 -filter_complex [0:v]trim=0:15,setpts=PTS-STARTPTS[v0]; [1:v]trim=0:5,setpts=PTS-STARTPTS[v1]; [0:v]trim=20:30,setpts=PTS-STARTPTS[v2]; [v0][v1][v2]concat=n=3:v=1:a=0[out] -map [out] output.mp4
It doesn't work since ; means end of command in bash.
Putting quotes back in java code should fix command (make sure to escape them properly).
String c1=" -i "+dir+"sample.mp4 "+"-i "+dir+"ad.mp4 -filter_complex \"[0:v]trim=0:15,setpts=PTS-STARTPTS[v0]; [1:v]trim=0:5,setpts=PTS-STARTPTS[v1]; [0:v]trim=20:30,setpts=PTS-STARTPTS[v2]; [v0][v1][v2]concat=n=3:v=1:a=0[out]\" -map [out] "+dir+"output.mp4";
RunCommand("ffmpeg"+c1);
Trying to build a basic launcher for a java game. I'm building the proper command to run the application. When the following command executes in the launcher, the launcher closes as expected, but the command doesn't appear to work - either it doesn't work or the game launches and immediately crashes.
When I print this same command to the console and copy/paste it into console and execute manually, it works perfectly.
/**
*
*/
protected void launch(){
currentStatusMsg = "Launching...";
String cmd = "java -jar";
cmd += " -Djava.library.path=\"" +nativesDirectory.getAbsolutePath() + "\"";
cmd += " \""+applicationJar.getAbsolutePath() + "\"";
System.out.println(cmd);
try {
Runtime rt = Runtime.getRuntime();
Process pr = rt.exec(cmd);
//closeLauncher();
BufferedReader input = new BufferedReader(new InputStreamReader(pr.getInputStream()));
String line=null;
while((line=input.readLine()) != null) {
System.out.println(line);
}
int exitVal = pr.waitFor();
System.out.println("Exited with error code "+exitVal);
} catch(Exception e) {
System.out.println(e.toString());
e.printStackTrace();
}
}
I tried adding something to read the output, but nothing is printed.
I was originally using the following format instead, but it has the same effect:
Process pr = Runtime.getRuntime().exec(new String[]{
"java",
"-Djava.library.path=\"" +nativesDirectory.getAbsolutePath() + "\"",
"-jar",
applicationJar.getAbsolutePath()});
Update I realized I was closing the launcher before allowing the debug code to run. The system only prints: "Exited with error code 1"
I finally was able to get the subprocess error to print. It states:
Exception in thread "main" java.lang.UnsatisfiedLinkError: no lwjgl in java.library.path
However, it should be available because the command I'm executing includes the library path, and when this exact command is run manually, it works fine.
the java command launcher is not a shell. don't use quoting and space separated commands because it won't end well. put each argument into a separate String without any extra quoting, and use the exec(String[]) method.
I am writing a java app that needs to perform mysql dump, and I am using the runtime.exec, based in the when runtime.exec won't article. The code is below:
public int exectuteCommand(){
Runtime rt = Runtime.getRuntime();
logger.debug("exexuting cmd: " + showCommand());
int exit = -1;
try {
Process proc = rt.exec(cmd);
ExtProcessStreamHandler errorHandler = new ExtProcessStreamHandler(proc.getErrorStream(), "ERROR");
ExtProcessStreamHandler outHandler = new ExtProcessStreamHandler(proc.getInputStream(), "OUTPUT");
// kick it off
errorHandler.start();
outHandler.start();
exit = proc.waitFor();
} catch (IOException e) {
logger.error("ERROR!! ~~ executing command " + showCommand(), e);
e.printStackTrace();
} catch (InterruptedException e) {
logger.error("ERROR!! ~~ unexpected return for " + showCommand() + " , returned " + exit, e);
e.printStackTrace();
}
return exit;
}
1) The command that the process returns works in the shell (I'm running this on a mac). The first error I had was an inability to find the mysqldump command. That results in this error:
java.io.IOException: Cannot run program "mysqldump": error=2, No such file or directory
I resolved that by adding the complete path of the file to the command. The $PATH var shows
/usr/local/mysql/bin/mysqldump
as the complete path. How can I make sure my java app has that info?
2) when adding the complete path to the command, I get this error msg:
INFO [Thread-1] (ExtProcessStreamHandler.java:28) - external process ERROR : mysqldump: Couldn't find table: ">"
Here is the code that builds the command array:
return new String[] {MYSQLDUMP_CMD, "-u", USER_DEFAULT, "-p"+ PW_DEFAULT, TEST_DB_NAME,
">", DUMP_LOC};
again, when I copy the command passed to the java app into the shell on my mac, it works. Not sure what I'm doing wrong.
thanks in advance!
It thinks ">" is an argument intended for mysqldump. You are invoking an executable, not evaluating a shell expression. If you want to pipe your output, do it with the outHandler and errorHandler in your code.
An alternative is to invoke a shell and pass the expression you want to evaluate as an argument:
expr = new StringBuilder()
.append(MYSQLDUMP_CMD).append(' ')
.append("-u").append(USER_DEFAULT).append(' ')
.append("-p").append(PW_DEFAULT).append(' ')
.append(TEST_DB_NAME).append(' ')
.append(">").append(' ')
.append(DUMP_LOC)
.toString();
return new String[] {"/bin/bash", "-c", expr};
If your code to build the command array doesn't wrap spaced arguments in single quotes (or if the JDK doesn't do this for you), then modify the StringBuilder statement to create the wrapped quotes for you.
Below Code is worked for me
public static void backup() {
String currentDate = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy_MM_dd"));
String backupPath = String.format("%s/%s.%s", Helper.BACKUP_PATH, currentDate, "sql");
File backupFile = new File(backupPath);
if (!backupFile.exists()) {
try {
backupFile.createNewFile();
String mysqlCom=String.format("mysqldump -u%s -p%s %s",USER_NAME,PASSWORD,DB);
String[] command = new String[] { "/bin/bash", "-c",mysqlCom};
ProcessBuilder processBuilder = new ProcessBuilder(Arrays.asList(command));
processBuilder.redirectError(Redirect.INHERIT);
processBuilder.redirectOutput(Redirect.to(backupFile));
Process process = processBuilder.start();
process.waitFor();
LOGGER.info("Backup done");
} catch (IOException e1) {
e1.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
LOGGER.info("Database already backuped today");
}
}
I want to run aapt using the Java runtime. My code is as follows:
String srcFile = "/home/jay/testing_FILES.apk";
String dsFile = "/home/jay/testing_FILES";
String firstCommand = ("/home/jay/android-sdk-linux/platform-tools/aapt " +
"package -u -f -F" + srcFile + dsFile);
try {
Runtime rt = Runtime.getRuntime();
Runtime.getRuntime().exec(firstCommand);
} catch (IOException e1) {
e1.printStackTrace();
}
This code doesn't give me an error, but it doesn't give me any results either. Any thoughts on how to fix this? Also this is meant to be portable code so if there is a way to do it without scripting that would be preferred.
You are trying to put the arguments in the same string as the command; that won't work here because there is no shell to parse them out.
You might want to try the form of exec() which takes a string array containing a command and it's arguments.