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

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

Related

Run a compound shell command from Java/Groovy

I got stuck trying to run a compound shell command from a Groovy script. It was one of those commands where you separate with "&&" so that the 2nd command never runs if the 1st one fails. For whatever reason I couldn't get it to work. I was using:
println "custom-cmd -a https://someurl/path && other-cmd -f parameter".execute([], new File('/some/dir')).text
The shell kept misinterpreting the command throwing errors like "custom-cmd -f invalid option" It was like it was ignoring the "&&" in between. I tried using a semi-colon as well but was not lucky. I tried using straight Java APIs Runtime.getRuntime().exec() and splitting the command into an array. I tried wrapping the command in single quotes and giving it to '/bin/sh -c' but nothing works.
How do you run a compound shell command from Java? I know I've done this in the past but I cannot figure it out today.
With groovy, the list form of execute should work:
def out = ['bash', '-c', "custom-cmd -a https://someurl/path && other-cmd -f parameter"].execute([], new File('/some/dir')).text
Of course you may want to use the consumeProcessOutput method on process, as if the output is too large, calling text may block
Try something like:
Runtime.getRuntime().exec("cmd /c \"start somefile.bat && start other.bat && cd C:\\test && test.exe\"");
Runtime.getRuntime().exec() can be used without splitting the commands into an array.
see https://stackoverflow.com/a/18867097/1410671
EDIT:
Have you tried using a ProcessBuilder? This seems to work on my OSX box:
public static void main(String[] args) throws IOException {
ProcessBuilder builder = new ProcessBuilder( "/bin/sh", "-c", "echo '123' && ls" );
Process p=null;
try {
p = builder.start();
}
catch (IOException e) {
System.out.println(e);
}
Scanner s = new Scanner( p.getInputStream() );
while (s.hasNext())
{
System.out.println( s.next() );
}
s.close();
}

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.

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.

Executing Multi cmd Command using JAVA

Am trying to pop up a CMD then execute some Commands such as:
echo SOMETHING && echo SOMETHING && mkdir....etc
i managed to open the CMD but JAVA doesnt seem to recognize "&" inside string
the first command which before the "&" is the only being executed
any tips ?
try {
// Execute command
String command = "cmd /c start echo hello baby & echo the world";
Process child = Runtime.getRuntime().exec(command);
// Get output stream to write from it
} catch (IOException e) {
}
}
}
It's &&, not &.
A double ampersand is a logical "and", a single ampersand means "put the (first!) process into the background".
PS: By "cmd", do you mean the good ol' Windows cmd.exe? If yes, I don't know if those ampersands are working there. Try the PowerShell instead if so.

Execute Unix system command from JAVA problem

I am facing a weird issue with executing a system command from JAVA code.
Actually i want to get the Mac OSX system information from my JAVA App.
For that im using
Runtime.getRuntime().exec("system_profiler -detailLevel full");
This is working fine.If i print the output,it is cool.
But i want to write this information to a plist file for future use.For that im using the -xml argument of system_profiler.like,
String cmd = "system_profiler -detailLevel full -xml > "+System.getProperty( "user.home" )+"/sysinfo.plist";
Process p = Runtime.getRuntime().exec(cmd);
Basically this should create a plist file in the current users home directory.
But this seems to be not writing anything to file.
Am i missing something here ?
My Java is more than rusty, so please be gentle. ;-)
Runtime.exec() does not automatically use the shell to execute the command you passed, so the IO redirection is not doing anything.
If you just use:
"/bin/sh -c system_profiler -detailLevel full > path/file.plist"
Then the string will be tokenized into:
{ "/bin/sh", "-c", "system_profiler", "-detailLevel", "full", ">", "path/file.plist" }
Which also wouldn't work, because -c only expects a single argument.
Try this instead:
String[] cmd = { "/bin/sh", "-c", "system_profiler -detailLevel full > path/file.plist" };
Process p = Runtime.getRuntime.exec(cmd);
Of course, you could also just read the output of your Process instance using Process.getInputStream() and write that into the file you want; thus skip the shell, IO redirection, etc. altogether.
Christian.K is absolutely correct. Here is a complete example:
public class Hello {
static public void main (String[] args) {
try {
String[] cmds = {
"/bin/sh", "-c", "ls -l *.java | tee tmp.out"};
Process p = Runtime.getRuntime().exec (cmds);
p.waitFor ();
System.out.println ("Done.");
}
catch (Exception e) {
System.out.println ("Err: " + e.getMessage());
}
}
}
If you weren't using a pipe (|) or redirect (>), then you'd be OK with String cmd = "ls -l *.java", as in your original command.
If you actually wanted to see any of the output in your Java console window, then you'd ALSO need to call Process.getInputStream().
Here's a good link:
Running system commands in Java applications

Categories

Resources