United StatesChange Country, Oracle Worldwide Web Sites Communities I am a... I want to...
JDK-4064116 : Runtime.exec(String[]) collapses white spaces on Win32

Details
Type:
Bug
Submit Date:
1997-07-11
Status:
Closed
Updated Date:
1999-01-15
Project Name:
JDK
Resolved Date:
1999-01-15
Component:
core-libs
OS:
solaris_2.4,solaris_2.6,windows_nt,windows_95
Sub-Component:
java.lang
CPU:
x86,sparc
Priority:
P3
Resolution:
Fixed
Affected Versions:
1.1,1.1.2,1.1.3,1.1.5
Fixed Versions:
1.2.0 (1.2beta3)

Related Reports
Duplicate:
Relates:

Sub Tasks

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
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
                                     
1998-02-03
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.
                                     
2004-06-11
CONVERTED DATA

BugTraq+ Release Management Values

COMMIT TO FIX:
generic

FIXED IN:
1.2beta3

INTEGRATED IN:
1.2beta3


                                     
2004-06-14



Hardware and Software, Engineered to Work Together