Java Runtime exec() fails to escape characters properly
Asked Answered
S

4

18

This might already been answered before but that was regarding unicode and I don't think this is unicode (it's in ASCII so...).

When I execute this in my terminal there is no problem what so ever

vboxmanage setextradata "Test Machine" "VBoxInternal/Devices/pcnet/0/LUN#0/Config/ssh/HostPort" 2222

However when I use the following in Java

Runtime.getRuntime().exec("vboxmanage setextradata \"Test Machine\" \"VBoxInternal/Devices/pcnet/0/LUN#0/Config/ssh/HostPort\" 2222");

It returns an error: unregistered vm '"TestMachine"'

The same goes for parameters with spaces in them like Test\ Machine, then it doesn't escape the space.

Now I think this has something to do with the character encoding, but I don't see any option to set that o_O

Sabbat answered 11/5, 2011 at 19:43 Comment(4)
doesn't look like an encoding issue. See ProcessBuilder on how to specify arguments to a program.Cartridge
Why would TestMachine need to be in quotes or escaped?Jimmyjimsonweed
Because this is inside a general function and there is a possibility that there will be VM names with spaces, therefore we need the quotes. I'll change it to make it more clear.Sabbat
possible duplicate of How to execute command with parameters?Trademark
L
21

You are calling the program and its arguments in one pass, which effectively shoves the entire string into the processing environment (hoping for the best).

In Windows systems, the operating system makes a point of handling the executable and arguments differently, and putting them all in the same string just requires you to pick the perfect string which the environment (of which there are two that i know about) can re-parse into an argument list. The better solution is to use

Runtime.exec(new String[] {"vboxmanage", "setextradata", "Test Machine", "VBoxInternal/Devices/pcnet/0/LUN#0/Config/ssh/HostPort", "2222"});

which corresponds to Runtime's

public Process exec(String[] cmdarray)
         throws IOException

At best, with the one string method you'll eventually find out how to hint and escape out the arguments so they don't get treated as part of the executable name, but then you'll encounter a new issue, that they get passed all as one parameter to the executable. Once you fix that depending on the environment, you'll either find that your quotes aren't getting stripped from the parameters (leading to parameters like "2222") or that Window's semi-broken argument parsing is going to parse on whitespace first (leading to parameters like ("Test). It doesn't make sense, that's why there are more than one exec methods available.

Lockout answered 11/5, 2011 at 20:27 Comment(2)
Thanks for the great extra explination :)Sabbat
Very unfortunately none of the solutions that use the "chunked" interfaces to Runtime.exec or ProcessBuilder are applicable in my particular scenario. I receive the command line as a whole from configuration (coming from the user), and I am supposed to substitute variables in it before executing it. Example: sometool -i ${current_file_path}. It is my responsibility to make sure that the path variable expands to a single and entire command line argument, but I cannot (be bothered to) parse the entire command line so I can feed it to those chunked APIs.Inga
A
8

On Windows, Runtime.exec(String[]) is unsafe as well. If the argument array contains the empty string, the empty argument is basically omitted which may lead to an invalid command line. Also, quotes within any of the arguments are not prefixed with backslashes. Also, the ProcessBuilder may add quotes to arguments without doubling the number of preceding backslashes which gives funny results if one passes a path to a folder like c:\program files\ including the trailing backslash.

Microsoft's documentation of their command line tokenizer is available here: http://msdn.microsoft.com/en-us/library/a1y7w461.aspx

The issues I described are documented here: https://bugs.java.com/bugdatabase/view_bug?bug_id=6468220 and https://bugs.java.com/bugdatabase/view_bug?bug_id=6518827

Alvinalvina answered 4/12, 2011 at 18:46 Comment(0)
P
6

Using Runtime.exec(String) is the wrong method for this case (and generally the wrong method for passing arguments to the new process).

Use one of the exec overloads that takes an array of arguments to pass to the new process. (Or, see Paul's answer with ProcessBuilder -- neither of these approaches suffer from escaping issues).

Example:

exec(new String[] { "vbomanager",
  "setextradata", "Test Machine",
  "VBoxInternal/Devices/pcnet/0/LUN#0/Config/ssh/HostPort", "2222"});

Happy coding.

Picayune answered 11/5, 2011 at 20:18 Comment(1)
Scary, you and I just typed out (almost) the same code sample. I know it's because it is the "right" answer, but it still took me back for a moment.Lockout
A
5

Rather than trying to quote the command line you might be better off using ProcessBuilder.command(arg0, arg1, ...)

See ProcessBuilder for details.

Aerotherapeutics answered 11/5, 2011 at 20:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.