Can't execute Shellscript successfully using getruntime.exec() - java

I have written a code to execute a script from java :
String wrapper_script=homedir+"/blast_distribute.sh "+" --seqs="+seqs+" --i="+formobj.getUpFile().getFileName()+" "+formobj.getSelected_program();
script_exec=Runtime.getRuntime().exec(wrapper_script);
This works perfect for me as command is executed successfully. Now I need to run this command as other user so I need to execute a command in a format like :
su username -c 'command'
SO I have edited above script_exec string as :
String wrapper_script1="su - "+username+" -c "+"'"+wrapper_script+"'";
I have printed wrapper_script1 which diplays:
su - abhijeet -c '/home/abhijeet//blast_distribute.sh --seqs=1562
--i=mPS_0.contigs.fasta'
If I run same command directly on Linux,It works exactly as I need.But When I am running through :
script_exec=Runtime.getRuntime().exec(wrapper_script1);
It does't work properly ,In my error stream I get error as
su: unrecognized option '--seqs=1562' Try `su --help' for more
information.
I have tried a lot but could't resolve the issue.What can be the reason for this issue?

You should use multiple parameters run for this:
Runtime.getRuntime().exec(new String[] {"su", "-", username, "-c",
homedir + "/blast_distribute.sh " + " --seqs=" + seqs + " --i=" + formobj.getUpFile().getFileName() + " " + formobj.getSelected_program()
});
You're getting the error, because ' is not treated here as a enclosing characters, but as a parameters for su command.
Let's look into details. When you type into console some command like su - jsmith -c 'aba --cabga', what you're doing is: "run command su with such an arguments: -, jsmith, -c, aba --caba (one argument for multiple words)".
The same thing you should do in your Java code. There's special command in Java API for running the concrete command with concrete arguments instead of parsing line as shell do: Runtime.exec(String[]): the first argument is command, the next are the arguments. That's what we do in this code block.

Related

Executing mv command with Java.exec();

I am trying to move an file via the linux mv command.
I have following code:
processBuilder.command("/bin/sh", "-c", "\"mv", "\"" + rawOutput + "/" + dir + "/build/libs/*\"", "\"" + startDir + "/test.jar\"\"");
During execution it translates to
/bin/sh -c "mv "/home/test/update/Test-test-23afi369890Ajk/build/libs/*" "/home/test/test.jar""
While this command executes perfectly via the command line, it fails during execution of the jar.
Following error is given:
"/home/test/update/Test-test-23afi369890Ajk/build/libs/*": 1: "/home/test/update/Test-test-23afi369890Ajk/build/libs/*": Syntax error: Unterminated quoted string
Thanks in advance!
Here's the shell command you want to execute:
mv /home/test/update/Test-test-23afi369890Ajk/build/libs/* /home/test/test.jar
Here's the Java concatenation that builds this shell command in Java (you should print and copy-paste it to a shell to verify it):
String myCmd = "mv " + rawOutput + "/build/libs/* " + dir + "/test.jar";
Here's how you can run it in a shell:
processBuilder.command("/bin/sh", "-c", myCmd);
This gives the following argument list:
/bin/sh
-c
mv /home/test/update/Test-test-23afi369890Ajk/build/libs/* /home/test/test.jar
And for completeness, here's how you should have designed it, with a static command string and passing in the arguments as separate parameters to avoid shell injection:
String myCmd = "mv \"$1\"/build/libs/* \"$2\"/test.jar";
processBuilder.command("/bin/sh", "-c", myCmd, "_", rawOutput, dir);
Whose argument list is:
/bin/sh
-c
mv "$1"/build/libs/* "$2"/test.jar
_
/home/test/update/Test-test-23afi369890Ajk
/home/test
(the _ becomes $0, i.e. the script's filename for use in error messages and similar)
During execution [my attempt] translates to
/bin/sh -c "mv "/home/test/update/Test-test-23afi369890Ajk/build/libs/*" "/home/test/test.jar""
I don't know how you reached this conclusion. The actual argument list it expands to is:
/bin/sh
-c
"mv
"/home/test/update/Test-test-23afi369890Ajk/build/libs/*"
"/home/test/test.jar""
This corresponds to the following shell command which probably fails with the same error messages as your Java program (if you're not suppressing stderr):
$ /bin/sh -c '"mv' '"/home/test/update/Test-test-23afi369890Ajk/build/libs/*"' '"/home/test/test.jar""'
"/home/test/update/Test-test-23afi369890Ajk/build/libs/*": 1: Syntax error: Unterminated quoted string

Java Runtime exec() not working

I try to execute a shell command via java like this
if (Program.isPlatformLinux())
{
exec = "/bin/bash -c xdg-open \"" + file.getAbsolutePath() + "\"";
exec2 = "xdg-open \"" + file.getAbsolutePath() + "\"";
System.out.println(exec);
}
else
{
//other code
}
Runtime.getRuntime().exec(exec);
Runtime.getRuntime().exec(exec2);
but nothing happens at all. When I execute this code it prints /bin/bash -c xdg-open "/home/user/Desktop/file.txt" in the console, but does not open the file. I have also tried to call the bash first and then the xdg-open-command, but there is not change.
What's the problem here and how can I solve this?
EDIT: The output of the calling looks like this:
xdg-open "/home/user/Desktop/files/einf in a- und b/allg fil/ref.txt"
xdg-open: unexpected argument 'in'
But this seeems very strange to me - why is the command seperatet before the in even the entire path is set in quotation marks?
Please note that you don't need xdg-open to do this.
You can use the java platform-agnostic Desktop API:
if(Desktop.isDesktopSupported()) {
Desktop.open("/path/to/file.txt");
}
Update
If the standard approach still gives issues, you can pass the parameters as an array since Runtime.exec does not invoke a shell and therefore does not support or allow quoting or escaping:
String program;
if (Program.isPlatformLinux())
{
program = "xdg-open";
} else {
program = "something else";
}
Runtime.getRuntime().exec(new String[]{program, file.getAbsolutePath()});

Redirecs with /bin/bash from Java Runtime exec()

I'm trying to use a redirect in a command executed with the exec()-method of java.lang.Runtime on Ubuntu 14.04
public static void main(String[] args) throws IOException, Exception {
Runtime runtime = Runtime.getRuntime();
String command = "echo bla > bla.txt";
System.out.println("Command : " + command);
Process process = runtime.exec(command);
printLines(" stdout", process.getInputStream());
printLines(" error", process.getErrorStream());
process.waitFor();
System.out.println("ExitValue : " + process.exitValue());
}
private static void printLines(String name, InputStream ins) throws Exception {
try(Stream<String> lines = new BufferedReader(new InputStreamReader(ins)).lines()) {
lines.forEach(line -> System.out.println(name + " : " + line));
}
}
The output is:
Command : echo bla > bla.txt
stdout : bla > bla.txt
ExitValue : 0
So bla > bla.txt is written to stdout but of course there is no redirect to bla.txt.
Perhapes shell redirects are not possible in a simple exec().
So I tried to change the command = "/bin/bash -c 'echo bla > bla.txt'" to use the whole echo and redirect as a parameter to /bin/bash.
With this I get the result:
Command : /bin/bash -c 'echo bla > bla.txt'
error : bla: -c: line 0: unexpected EOF while looking for matching `''
error : bla: -c: line 1: syntax error: unexpected end of file
ExitValue : 1
Of course /bin/bash -c 'echo bla > bla.txt'works fine on Ubuntu and creates the desired file.
I found no place where I could set the single quotes to get a satisfying result, and I also tried with all kinds of escape characters to escape the spaces or the redirect (>).
It works if I use a command array like
String cmdArray[] = {"/bin/bash", "-c", "echo bla > bla.txt"};
Process process = runtime.exec(cmdArray);
, but it has to be a single string because the entire command has to be build somewhere else.
I surely know
that there are better ways to write strings to files,
that it may be a bad idea to execute commands build somewhere else,
and so on ...
I'm just curious why this does not work.
The cause is that exec uses simple StringTokenizer with any white space as a delimiter to parse the actual command. Therefore it's portable as it does work nowhere when you pass something complex :-)
The workaround you chose is correct way, portable and most of all safest asyou ddon't need to escape if the command contained for example quotes etc.
String command = "echo bla > bla.txt";
Process process = runtime.exec(command);
The > blah.txt text in your command is shell syntax to redirect standard output. It's implemented by shells like sh or bash.
Runtime.exec() doesn't use a shell to run commands. The way it launches commands is described here and here. Basically, it uses its own logic to split the command string into arguments at spaces, then directly executes the resulting command.
If you want to invoke a command which should be interpreted as a shell command, you need to explicitly invoke a shell:
String[] command = { "/bin/bash", "-c", "echo bla > bla.txt" };
Process process = runtime.exec(command);

How can I correctly escape string for awk input using Java?

I'm trying to execute the following command (which works on the terminal) within Java:
awk -F';' 'NR>1{gsub(/; +/,";",$0);printf("{msisdn:\"%s\",imei:\"%s\",brand:\"%s\",model:\"%s\",sap:\"%s\",sap_cod:\"%s\",file_name:\"teste\",company:{\"$ref\":\"company\",\"$id\":ObjectId\"456\")}}\n",$2,$15,$16,$17,$18,$20)}' /Users/milena/Desktop/giant.csv
The Java code I am using is this:
String fileName = "test";
String company = "456";
String awk = "awk -F';' 'NR>1";
String gsub = "{gsub(/; +/,\";\",$0);";
String printf = "printf(\"{msisdn:\\\"%s\\\",imei:\\\"%s\\\",brand:\\\"%s\\\","
+ "model:\\\"%s\\\",sap:\\\"%s\\\",sap_cod:\\\"%s\\\",file_name:\\\""+fileName+"\\\",company:"
+ "{\\\"$ref\\\":\\\"company\\\",\\\"$id\\\":ObjectId\\\""+company+"\\\")}}\\n\",$2,$15,$16,$17,$18,$20)}\' ";
String path = "/Users/milena/Desktop/giant.csv";
String command = awk + gsub + printf + path;
Process p
p = Runtime.getRuntime().exec(command);
The error I am getting is:
awk: syntax error at source line 1
context is
>>> ' <<<
missing }
missing )
awk: bailing out at source line 1
Any ideas of what am I doing wrong?
When you use Runtime.getRuntime().exec(command), it takes the string and breaks it into a command and arguments based on spaces.
This simple parsing is not the same as the parsing done by the shell when you invoke the command. The shell, for example, takes quotes into consideration. This means that if you have a command line like:
cmd 'abc' 'def'
The arguments that the shell will send to the cmd command are going to be abc and def. But if you give the same command to Runtime.getRuntime().exec(command), it will send 'abc' and 'def' to cmd as arguments. Yes, including the quotes!
The situation gets worse if you have spaces in any of the arguments. If the shell gets
cmd 'my single argument'
It will invoke the command with a single argument my single argument. But Runtime.getRuntime().exec(command) will invoke cmd with three arguments: 'my, single and argument'!
So it's not recommended to use this particular overload for anything but very simple commands. Instead, you should use the overload that accepts an array of strings. The first element should be the command name, and each argument should be in a separate element:
String[] command = { "awk",
"-F;",
"NR>1{gsub(/; +/,\";\",$0);printf(\"{msisdn:\\\"%s\\\",...",
"/Users/milena/Desktop/giant.csv"
};
Process p = Runtime.getRuntime().exec(command);
Note: It is recommended to use the ProcessBuilder class to build a Process rather than Runtime.getRuntime.exec - it gives you better control of the command and you can pass the separate arguments right to the constructor.

Execute command from java which might include spaces

I am trying to execute the following command from a java program:
java -jar /opt/plasma/fr.inria.plasmalab.plasmalab-1.3.4.jar -t -a montecarlo -A"Total samples"=1000 -m models/translated/plasma/NaCl2.rml:rml --format csv -r models/translated/plasma/NaCl2.bltl:bltl
with the following code:
String totalSample = "-A\"Total samples\"=1000";
String mcCommand = "java -jar " + MChecker.getAppPath() + " -t "
+ "-a " + "montecarlo " + totalSample
+ " -m " + mcModelRelPath + ":rml " + "--format " + "csv "
+ "-r " + customQueryRelPath + ":bltl";
Process process = Runtime.getRuntime().exec(mcCommand);
int errCode = process.waitFor();
//then get the output, and error
But it results in the following error:
Wrong parameter description : Dynamic parameter expected a value of the form a=b but got:"Total
I ran the same command in a terminal and it worked without any problem. But when I create the command in Java and try to invoke the tool it does not work.
I think it's confused because of the totalSample parameter which includes a space. What I did next was to put "\ " space escape in paramater(String totalSample = "-A\"Total\\ samples\"=1000";), but it still refused to accept it. It gave the following error:
Wrong parameter description : Dynamic parameter expected a value of the form a=b but got:"Total\
Then I run the same parameters with the ProcessBuilder object, like the following:
String[] mcCommand = {"java", "-jar", MChecker.getAppPath(), "-t",
"-a", "montecarlo",totalSample, "-m",
mcModelRelPath + ":rml", "--format", "csv", "-r",
customQueryRelPath + ":bltl" };
ProcessBuilder pb = new ProcessBuilder(mcCommand);
Process process = pb.start();
process.waitFor();
But it still did not work and threw some custom exceptions.
I am currently out of options -- do you have any idea why this command does not work with Java, when it works just fine from the terminal interface?
BTW: I ran the same code on Windows it worked perfectly, but I have to run this code on Ubuntu OS.
Many Thanks
It was weird that, Java Process and ProcessBuilder classes could not pass the parameters properly. I don't know why, but since I was able to execute the command from terminal. I decide to call the terminal first and then execute the command. Therefore, I changed my command as following
String mcCommand[] = {
"/bin/sh",
"-c",
"java -jar /opt/plasma/fr.inria.plasmalab.plasmalab-1.3.4.jar -t -a montecarlo "+totalSample+" -m models/translated/plasma/NaCl2.rml:rml --format csv -r models/translated/plasma/NaCl2.bltl:bltl" };
Despite it is platform dependant solution, currently it is ok for me.
Don't add extra quotes for Total samples when using ProcessBuilder:
ProcessBuilder pb = new ProcessBuilder("java", "-jar", MChecker.getAppPath(), "-t",
"-a", "montecarlo", "-ATotal samples=1000",
"-m", mcModelRelPath + ":rml", "--format", "csv",
"-r", customQueryRelPath + ":bltl");
//...

Categories

Resources