0

I try to call a vim editor with a file on a specific position from within a java process:

Runtime.getRuntime().exec("gvim /etc/fstab '+normal GW'");

Unfortunately that doesn't work. It gives a strange error message from gvim which doesn't help finding the problem ("/etc/fstab" "/etc/fstab" [readonly] 12L, 664C).

The same command directly entered into a terminal works well.

I tried to modify the command:

Runtime.getRuntime().exec("gvim /etc/fstab +normal\ GW");

which also works when entered directly in a terminal. But this gives the exact same behaviour.

What does work ist splitting the command and its parameters into a String[]:

Runtime.getRuntime().exec(new String[]{"gvim", "/etc/fstab" ,"+normal GW"});

What is the reason for this behaviour? How can I call the above mentioned command as a single string from within Java?

radlan
  • 1,985
  • 4
  • 24
  • 46

2 Answers2

1

According to documentation exec(String command) is treating whole string as a command, if we look at the documentation

public Process exec(String command)
         throws IOException

Executes the specified string command in a separate process.

This is a convenience method. An invocation of the form exec(command) behaves in exactly the same way as the invocation exec(command, null, null).

Parameters:

command - a specified system command.

And coming to exec(String[] cmdarray) it will treat cmdarray as array containing the command to call and its arguments.This is the only difference between these two methods which might be the issue

public Process exec(String[] cmdarray)
         throws IOException

Executes the specified command and arguments in a separate process.

This is a convenience method. An invocation of the form exec(cmdarray) behaves in exactly the same way as the invocation exec(cmdarray, null, null).

Parameters:

cmdarray - array containing the command to call and its arguments.

Deadpool
  • 29,532
  • 9
  • 38
  • 73
1

exec is not a complete shell.

As a convenience, it does allow you to pass a single String with both executable name and parameters (as opposed to using the String[] version), but the logic that it uses to split that String is very simple, it just calls new StringTokenizer(command), which just splits by whitespace and has no understanding of special quoting and escaping behaviour a full command shell would offer.

So your single quotes are not being respected.

gvim /etc/fstab '+normal GW'   // last quoted section supposed to be a single arg

becomes

String[]{ "gvim", "/etc/fstab", "'+normal", "GW'" } // four args instead of three

How can I call the above mentioned command as a single string from within Java?

Do you really need this? Unless the application dynamically accepts command line input from an interactive user, it should be possible to split the arguments when you write the program. Much safer that way.

If you really need this, you could pass your whole string to a command shell.

Try Runtime.getRuntime().exec(new String[]{"bash", "-c", theCommand });

But there is some overhead involved.

Other than that, you could try to find a Java library that can split the string "properly".

Thilo
  • 241,635
  • 91
  • 474
  • 626
  • Note that unlike StringTokenizer, the Stackoverflow syntax highlighter does understand the quotes in the first code block and colours the parameter properly. – Thilo Jul 12 '19 at 07:57
  • 1
    `Unless the application dynamically accepts command line input from an interactive user`. Unfortunately this is the case. My example was only one possible example. Since it should even work under Windows, I would not like to call a specific shell (like bash). Have to think a bit about that. But thanks for your explanation. That the string is tokenized in such a simple way is the actual problem. Maybe doing that myself could be a solution. – radlan Jul 12 '19 at 09:03
  • @radlan: look at https://stackoverflow.com/q/16722259/14955 and https://stackoverflow.com/q/3366281/14955 – Thilo Jul 12 '19 at 09:09