Executing mv command with Java.exec(); - java

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

Related

Java ProcessBuilder Calling Terminal Function with Nested Quotes?

I'm on Linux and am writing a subroutine to change the ID tag of an mp3 file.
This command works: kid3-cli -c "tag 1" -c "set Title "TITLE"" -c "set Artist "ARTIST"" FILENAME.EXT
My attempt at trying this in ProcessBuilder:
new ProcessBuilder(Arrays.asList("kid3-cli", "-c", "\"tag", "1\"", "-c", "\"set", "Title", "\"" + title + "\"\"",
"-c", "\"set", "Artist", "\"" + artist + "\"\"", path.toString()))
I also thought to treat the quotes as a single parameter:
new ProcessBuilder(Arrays.asList("kid3-cli", "-c", "\"tag 1\"", "-c", "\"set Title \"" + title + "\"\"",
"-c", "\"set Artist \"" + artist + "\"\"", path.toString())
Whether or not it was successful can be verified with:
kid3-cli -c "get" SONG.EXT
When I run the program (with a few helpful debugging statements, I get:
INFO: COMMAND: kid3-cli -c "tag 1" -c "set Title "Unfinished Cathedral"" -c "set Artist "DAN TERMINUS"" /home/sarah/Music/Indexing/Temp/DAN TERMINUS - Unfinished Cathedral.mp3
And the tags come up as:
(base) sarah#MidnightStarSign:~/Music/Indexing/Temp$ kid3-cli -c "get" DAN\ TERMINUS\ -\ Unfinished\ Cathedral.mp3
File: MPEG 1 Layer 3 128 kbps 44100 Hz Joint Stereo 4:38
Name: DAN TERMINUS - Unfinished Cathedral.mp3
Tag 2: ID3v2.3.0
Title
Artist
Album
Comment Visit http://dan-terminus.bandcamp.com
Date 2014
Track Number 9
Album Artist DAN TERMINUS
Picture: Cover (front) cover
I'm fairly certain that I'm doing something wrong with regards to the number of entries to give the processbuilder, but I'm not sure. How can I get it to behave properly?
EDIT 0:
Strangely enough,. this also fails (although it has the exact same output when the command is queried, and if run on bash, will change the tags):
new ProcessBuilder(Arrays.asList("bash", "-c", "\"kid3-cli -c \"tag 1\" -c \"set Title \"" + title + "\"\" -c \"set Artist \""
+ artist + "\"\"" + path.toString() + "\"");
Although it leads to the correct string: INFO: COMMAND: bash -c "kid3-cli -c "tag 1" -c "set Title "Unfinished Cathedral"" -c "set Artist "DAN TERMINUS""/home/sarah/Music/Indexing/Temp/DAN TERMINUS - Unfinished Cathedral.mp3"
There are two problems here:
Your command is not doing what you think it’s doing.
Double-quotes are appropriate for shells (like bash), not for direct execution in programs.
First, let’s look at the command-line version of your command:
This command works: kid3-cli -c "tag 1" -c "set Title "TITLE"" -c "set Artist "ARTIST"" FILENAME.EXT
Yes, it “works,” but not for the reasons you think. Nesting quotes inside quotes doesn’t just work magically. It would be nearly impossible for any parser to know which quote characters are the end of the argument and which ones are part of the argument.
What you’re really doing is this:
kid3-cli -c "tag 1" -c "set Title "TITLE"" -c "set Artist "ARTIST"" FILENAME.EXT
╰──┬───╯ ╰┬╯ ╰─┬─╯ ╰┬╯ ╰──────┬── ────╯ ╰┬╯ ╰────────┬─ ─────╯ ╰────┬─────╯
arg0 arg1 arg2 arg3 arg4 arg5 arg6 arg7
(program
path)
As you can see, each quoted argument is one argument. Which means you want to pass each quoted argument as a single parameter to ProcessBuilder, without those quotes. The double-quote characters are only special in a shell (like bash), where they indicate that a value with spaces should not be split by the shell into multiple arguments.
But there is more. When you do this:
"set Title "TITLE""
you are not specifying an argument with double-quotes in the argument. All of those double-quote characters are interpreted by the shell, so none of them will be passed to the kid3-cli program.
What actually is happening is you are concatenating a quoted string, an unquoted title, and then an empty quoted string, all into a single command-line argument. These are all identical:
"set Title "TITLE""
"set Title "TITLE
'set Title 'TITLE
set\ Title\ TITLE
So, those double-quotes were never being passed to the program at all.
ProcessBuilder doesn’t need any quotes, because it is not a shell; rather, it directly executes a program with the given arguments. You are already telling ProcessBuilder what constitutes a single argument to the program, simply by passing each such argument as a separate String parameter value.
(Side note: Arrays.asList is unnecessary. ProcessBuilder has a constructor that takes string arguments directly.)
new ProcessBuilder("kid3-cli",
"-c", "tag 1",
"-c", "set Title \"" + title + "\"",
"-c", "set Artist \"" + artist + "\"",
path.toString())
The kid3 man page seems to prefer that you use single quote characters for arguments to kid3 commands, which may make things a little easier to read and a little less confusing:
new ProcessBuilder("kid3-cli",
"-c", "tag 1",
"-c", "set Title '" + title + "'",
"-c", "set Artist '" + artist + "'",
path.toString())

/bin/sh -c not working with process builder

I am trying to run the following command via process builder:
ls -lrt my-directory | tail -1
I am using bin/sh -c when passing it in but cannot get it to work any way I try.(my custom directory depends on the function and is passed in as a string)
process builder("/bin/sh", "-c", "ls -lrt ", customDir, " | tail -1");
This gives the contents of the directory I am running the program out of. It's as if I simply typed ls (not even ls -lrt).
Both of
String temp = "ls -lrt " + customDir + " | tail -1";
process builder("/bin/sh ", "-c " temp);
String temp = "ls -lrt " + customDir;
process builder("/bin/sh ", "-c " temp, " | tail -1");
say that the directory doesn't exist even though I know it does. Removing /bin/sh -c simply gives me an error saying it couldn't understand the ls -lrt command which I understand. But I simply have no idea how to get it to produce the actual results I would get if I typed this command in seperately.
edit: the problem is not because of how I am calling process builder. I send the content to another function which calls process builder properly and relays its output.For simplicity, I just said process builder (...) in the examples.

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

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");
//...

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

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.

Categories

Resources