JDK-4064116 : Runtime.exec(String[]) collapses white spaces on Win32
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.lang
  • Affected Version: 1.1,1.1.2,1.1.3,1.1.5
  • Priority: P3
  • Status: Closed
  • Resolution: Fixed
  • OS:
    solaris_2.4,solaris_2.6,windows_95,windows_nt solaris_2.4,solaris_2.6,windows_95,windows_nt
  • CPU: x86,sparc
  • Submitted: 1997-07-11
  • Updated: 1999-01-15
  • Resolved: 1999-01-15
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other
1.2.0 1.2beta3Fixed
Related Reports
Duplicate :  
Relates :  
Description
Problem:
I have a java program that needs to exec another java program, and some of the args in the args[] must contain spaces (and other special chars).

Solution:
Use Runtime.exec(String[]).

This works correctly on Solaris, but not on Win32.
Apparently on Windows, it concatenates my String array and reparses, which is extremely problematic when the strings contain spaces.


Here is the test code:

import java.io.*;

public class EA1 {

  public static void main(String[] args) throws Exception {
    String jav = System.getProperty("java.home")+File.separator+"bin"+File.separator+"java";
    String cl = "EA2";
    String arg = "X Y";
    String[] argsx = {jav,cl,arg};
    Process p = Runtime.getRuntime().exec(argsx);
    InputStream is = p.getErrorStream();
    int x = is.read();
    while(x!=-1){
      System.err.write(x);
      x = is.read();
    }
    p.waitFor();
  }

}


public class EA2 {

  public static void main(String[] args) throws Exception {
    System.err.println("EA2:");
    for(int i=0;i<args.length;i++){
      System.err.println(i+": "+args[i]);
    }
    System.exit(0);
  }

}


On Solaris:
% java EA1 
EA2:
0: X Y


On Windows:
> java EA1
EA2:
0: X
1: Y


This is not pretty.

[fred.oliver@east]:
This is a hot issue for the JMAPI group right now.  We need to invoke one java
program from another on Win32, where the default classpath contains spaces, e.g.
"c:\Program Files\...".

Since the jre command on Win32 ignores the classpath variable (I would really
like to see the justification for that), we are required to supply the 
classpath as an argument on the command line.



Comments
CONVERTED DATA BugTraq+ Release Management Values COMMIT TO FIX: generic FIXED IN: 1.2beta3 INTEGRATED IN: 1.2beta3
14-06-2004

WORK AROUND The only workaround immediately obvious is to save the args to be passed into a file, using a special char to divide the members of the array. Then the only arg passed to the child would be the filename from which to read the real args. This only applies to java subprocesses. For real OS programs, there is NO workaround.
11-06-2004

EVALUATION The fix (see also comments) seems to be to add '"' if the user supplied arguments has a whitespace character. anand.palaniswamy@Eng 1997-09-08 I discussed this extensively with Dac and others, and this turns out to be a rathole. On Win32 an application is free to choose how it will handle command line arguments (it has three choices: - do nothing (no Unix like globbing). - do Unix like globbing with MS VC++'s setargv.obj - do Unix like globbing on its own). 1. Say we add quotes to exec'ing { "prog1", "a", "x y" } like this: "prog1 a \"x y\"" this would screw up prog1 if it was not linked with Microsoft's setargv.obj. Prog1 will now see random quotes, and this is difficult to debug, unless you write your own Echo that treats command line arguments just like Prog1. 2. The quoting in (1) also has a problem in that you can't write regular expression of the form _a *b_ (note: _ is just for clarity). Consider the above algorithm on ls: "ls \"a *b\"" Now this will fail to find files such as 'a 1b', 'a 2b'. So a more correct thing would be to quote around the spaces. "ls a\" \"*b" Tied in with this algorithm is that if you are already in quotes, don't quote whitespaces -- that way you don't break people that are already working around this bug. 3. Algorithm (2) becomes a nightmare to implement because you have to take care of mismatched quotes, and nested quotes. 4. Dac suggested a simpler algorithm that we first see if there are any quotes at all in the argument. If there are not any quotes, but there are spaces, then we quote the spaces. This sounds like the most reasonable thing to do. However it still does break programs that don't do any globbing, but understand single quotes (rare), and programs that don't grok any kind of quotes at all. Bottom line, anything we do has the risk of breaking code that is already out there. And Runtime.exec() is used pretty heavily -- the JCK harness relies on it, Marimba relies on it, and we really don't know how people are using this method. So if anything can be done, it has to be done in a major release such as 1.2, and not in a minor release like 1.1.5. Even if we want to "fix" this in 1.2, we will have to document it. Which means the specification of Runtime.exec() will have to talk about its behavior on Win32 -- something we want to avoid at all costs. In sum, I am somewhat inclined to mark this will-not-fix -- to argue this another way -- if you are doing a Runtime.exec(), then you are calling a program that is _not_ under Java's control, and at that point it is _your_ responsibility to figure out how to pass arguments to that application. I will get some more opinions on this before I change the state though. In the interim a workaround of figuring out which platform you are on (System.getProperty("os.name")) and then putting quotes appropriately yourself seems to be the right thing to do. anand.palaniswamy@Eng 1997-09-10 After doing some tests on win32, I found some of the above arguments are correct. 1. On unix, if we want to execute, say, "cat a*b", it is the responsible of shell to parse this single command line into argc and argv; On win32, the same "cat a*b" to be passed through to child process. And you can get it back using API GetCommandLine(); The C runtime will capture the single string and parse to argc and argv; This is the fundimental difference behind the problem; 2. The main() function application must use unix style globbing in c runtime; The WinMain() does not use any globbing, it gets the straight comamndline; 3. The #1 worry(see above) does not exist anyway. All crt on win32 must be able parse command line to argc and argv for all normal cases. Otherwise we will not even get main() to run. For example, if the above if we pass command line "prog a \"x y\"", any correct CRT MUST let main() function get "prog", "a" and "x y". Otherwise it is CRT bug, not ours. 4. The #2 worry(see above) will not appear on winnt 4.0 with microsoft crt. I run the test and pass "prog \"a *b\"", and the child process do get the "prog" and "a *b". I believe the CRT will pass through meta character like '?' and '*', because it has no way to understand it. 5. The bottom line is Runtime.exec() is to run a native application. Such an application may even does not have a main function(). It is a worthless effort to achieve the unix style and syntax. My suggested fix is we simply quote all arguements with space and tab. If people really want to pass meta character, like " ' \ / etc we need to use platform specific routine. On win32, we should recomment user to use Runtime.exec( String s), that is native windows application should expects. Documentation the difference will really help the user, though I know it is not good to add platform specific documentation.) ###@###.### 1998-02-03
03-02-1998