Runtime.exec on argument containing multiple spaces
Asked Answered
G

9

9

How can I make the following run?

public class ExecTest {
  public static void main(String[] args) {
    try {
      //Notice the multiple spaces in the argument
      String[] cmd = {"explorer.exe", "/select,\"C:\\New      Folder\\file.txt\""};

      //btw this works
      //String cmd = "explorer.exe /select,\"C:\\New Folder\\file.txt\"";

      //and surprisingly this doesn't work
      //String[] cmd = {"explorer.exe", "/select,\"C:\\New Folder\\file.txt\""};

      //Update: and (as crazy as it seems) the following also worked
      //String[] cmd = {"explorer.exe", "/select,\"C:\\New", "Folder\\file.txt\""};

      Runtime.getRuntime().exec(cmd);
    } catch (Exception e) {
        e.printStackTrace();
    }
  }
}

Using Java 6. Tested under Vista x64. By the way, taking the string that gets executed (you'll have to use the String version of exec to get it) and using it in the Search field of Vista's start menu will run as expected.

Godderd answered 13/7, 2011 at 22:46 Comment(2)
Is there a typo in the first case? I suspect that in second "btw" case, the /select is being treated oddly, because it's part of arg[0]. Providing a dir as an arg will open that folder. Given that they're all named the same, you could easily miss that it was one lower than you expected. I'd rename them to be different.Entourage
Well the first and second btw cases are the same commands. Here I 'm just demonstrating that the array version of exec fails in this case, while the string version worked. The path is there, and it's irrelevant i m afraid, i could have used anything.. thnx for answeringGodderd
G
7

Ok, this is not simply an update but also an answer so I'm filing it as one. According to all information I could find, the following should theoretically do it:

String[] cmd = {"explorer.exe", "/select,\"C:\New", "", "", "", "", "", "", "Folder\file.txt\""};

The multiple spaces have been broken into empty strings and the array version of exec is used. Using the above array, I debugged the loop in lines 50-75 of java.lang.ProcessImpl where a string is finally constructed. The resulting string was:

explorer.exe /select,"C:\New       Folder\file.txt"

This is what is passed as the 1st argument to ProcessImpl's native create method (line 118 same class), which as it seems fails to run properly this command.

So I guess it all ends here... sadly.

Thnx prunge for pointing out the java bug. Thnx everyone for their time and interest!

Godderd answered 16/7, 2011 at 2:22 Comment(1)
And now I'm looking for alternative ways to do this. Using jni is an option but leads to different versions of your app for 32 or 64 bit systems. Currently I'm looking into an insane solution, which in short is opening a bat file, writing the cmd in and executing the bat. Average total time is 45ms. That's excellent, the explorer.exe process takes about 500ms so this goes unnoticeable. Also this way you can exec literally everything. wdyt?Godderd
T
6

A miracle, it works!

Don't ask me why, but when i, after quite a while of nerve-wrecking research in the internets, was close to give up and use a temporary batch file as a workaround, i forgot to add the /select, parameter to the command, and, who would have thought, the following works on my Win 7 32Bit System.

String param = "\"C:\\Users\\ME\\AppData\\Local\\Microsoft\\Windows\\Temporary Internet Files\\\"";
try {
    String[]commands = new String[]{"explorer.exe", param};
    Process child = Runtime.getRuntime().exec(commands);
} catch (IOException e1) {
    System.out.println("...");
}

General Solution:

The solution of the bug-database mentioned by prunge in his post (https://bugs.java.com/bugdatabase/view_bug?bug_id=6511002) worked fine for me.

Reason:

Apparently the problem lies with the commenting of some characters done by java which it does before actually executing the command string. You have to do the commenting yourself by tokenizing your command string, to prevent the faulty java one to spring into action and mess everything up.

How to fix:

So, in my case i had to do the following (tokenizing my command string, so that no spaces are left inside the string):

String param[] = {
    "explorer.exe",
    "/select,C:\\Users\\ME\\AppData\\Local\\Microsoft\\Windows\\Temporary",
    "Internet",
    "Files\\"};

try {
    Process child = Runtime.getRuntime().exec(param);
} catch (IOException e1) {
    System.out.println("...");
}

As you can see i basically started a new String wherever a space occured, so "Temporary Internet Files" became "Temporary","Internet","Files".

Thunderstruck answered 16/11, 2012 at 15:45 Comment(0)
T
5

Always use Runtime.exec(String[]), not Runtime.exec(String) unless the command line is extremely simple.

Turley answered 14/7, 2011 at 4:25 Comment(14)
I will have to disagree based on my case, since using the array version fails while the string version works. You can uncomment my code and see for yourself. I know all about the tokenizer and the problem it creates, but it doesn't help in this case. And it really is a simple command right? So my opinion would be... with Runtime.exec, use whatever you manage to get it working!!Godderd
Minos that's because you have already compensated for the parsing in the single-arg version by quoting etc. If you use the other you only have to pass the actual values, no quotes, no second-guessing required.Turley
Don't really get it. How can this be written without escaping quotes? String[] cmd = {"explorer.exe", "/select,\"C:\\New Folder\\file.txt\""};. If you don't quote the path, and let exec do it for you then you 'll get the /select, part of the argument also quoted. Explorer will definitely not like this. Even if this is the only command with this awkwardness (not the case) this still seems buggy..when the string version works exactly as someone would expectGodderd
@Godderd {"explorer.exe", "/select", "C:\\New Folder\\file.txt"}.Turley
This will end up in the following command: explorer.exe /select "C\New Folder\file.txt". This, simply put is wrong. It is not the command I'm after. There is a space between /select and the path. Also in your example you've missed the comma after /select, but anyway the main problem is the space you introduced. Checking if explorer can handle the extra space, I see that it does, so I'll give a plus for your answer. But in general, I find it annoying that I cannot exec exactly the command I want. Why can't my string be left as is..Godderd
@Godderd The spaces in that line are between array elements, not inside them: they are ignored by the compiler, so they are not present at runtime to be passed to exec(). So i introduced no space. The entire purpose of this overload is to leave your arguments alone. If there is a comma missing, put it back. If it works what exactly are you annoyed about now?Turley
But my argument is only one:/select,"path" I quote the path cause this is what explorer.exe expects. This 1 argument does not have spaces anywhere but inside the path. So I don't get why you've separated /select, as a different array element. Why do you break the argument after select? The array version adds a space for each element of the cmdarray you give it. And that's what I see in java.lang.ProcessImpl. So you must be getting a space between /select, and the quoted path. Luckily explorer likes it. Btw, you realize we are not discussing an answer to the multiple spaces problem...Godderd
@Godderd No. the array version doesn't add any spaces. The cmd[] array becomes the argv[] of target take directly, no parsing, no spaces added, nothing. If that's all one argument, try {"explorer.exe", "/select,C:\\New Folder\\file.txt"}.Turley
EJP, please take a look at lines 50-75 in java.lang.ProcessImpl. In the first statement of the for loop, a single space is inserted for every element in cmdarray. I'm on Java 6 btwGodderd
@Godderd faIr enough, I am crazy again. Did the variant I just posted work?Turley
Well the variant will work unless the /select, gets quotes. To prevent that I had to put it together with the path as 1 argument. Then, explorer.exe requires a quoted path. So I have to escape only the path part of this 1 arg! Also I need to account for the bug pointed out by prunge, since my path has spaces! So, your variant works if written like this (finally!): String[] cmd = {"explorer.exe", "/select,\"C:\\New", "Folder\\file.txt"};. Crazy enough but works!Godderd
@Godderd {"explorer.exe", "/select,\"C:\\New Folder\\file.txt\""}? NB the 'bug' is still only a documentation RFE.Turley
I expected that to work initially but it didn't. And it's the rfe that causes the problem. Looking again into ProcessImpl, this is what you get: explorer.exe \"/select,\"C:\\New Folder\\file.txt\"\"" The arg has spaces, so it got quoted. No check was done to see if arg contains quotes.. only the first character is checked. So /select, is inside quotes now, and explorer.exe doesn't like thisGodderd
In my humble opinion EJP is correct in almost all cases. i had a similar problem where the space character was at different interval not all of them at the same time. Minos's method did not work for me at all. In my case the String Array method worked. Process Builder method works better when more control is needed and this problem is solved easily in that class. Although my comment does not answer the question but posting it for clarification as i found my problem's solution from EJP's suggestion.Turdine
J
2

Use new File(pathName).canExecute() first to check whether it's executable or not

EDIT:

public static void runAll(String... cmd)
{
    for(String s : cmd)
    {
        try
        {
            Runtime.getRuntime().exec(cmd);
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
    }
}

and then you can use it like: runAll("explorer.exe", "taskmgr.exe");

Jetblack answered 13/7, 2011 at 22:52 Comment(5)
yes explorer is an executable in vista. Are you not talking about explorer? Also, please see my comments. How come the other commands work?Godderd
@Godderd I don't understand your question well but see the update, is this what you're looking for?Jetblack
Sorry to ask, but did you try to run my main? My question is not on how to architect this, but why that completely simple code block I posted does not run as expected. Thnx..Godderd
@Godderd OK. well, maybe you have to run the program as administratorJetblack
Sure, but then why don't i need to be admin also in the non multiple spaces case? How come when there are only single spaces it works? I tried it anyway..Godderd
P
2

The characters ,-& and double spaces, all combined are a nightmare!

All the answers exposed here failed for "\\NAS\media\Music\Artistes\E\Earth, Wind & Fire\1992 - The eternal dance - Vol. 1 (1971-1975) (double space between 'Vol. 1' and '(1971').

I have no other choice than writing a temporary batch file:

   void openFolderOf( Album album ) {
      try {
         final String path = album._playList.getParent();
         final File batch = File.createTempFile( getClass().getSimpleName(), ".bat" );
         try( PrintStream ps = new PrintStream( batch )) {
            ps.println( "explorer.exe \"" + path + '"' );
         }
         Runtime.getRuntime().exec( batch.getAbsolutePath());
      }
      catch( final Throwable t ) {
         t.printStackTrace();
      }
   }

Note: on cmd.exe, the line explorer "\\NAS..." works well but not with Runtime.exec() nor ProcessBuilder.

Phrensy answered 30/9, 2013 at 19:37 Comment(1)
This also was the only way that worked for me, thanks but I used buffer writer instead: File batch = File.createTempFile( "bat" + cuid, ".bat" ); BufferedWriter txt = new BufferedWriter(new FileWriter(batch.getAbsolutePath())); txt.write(winrarcmd); txt.close();Hairworm
I
1

Could be a Java bug. See: https://bugs.java.com/bugdatabase/view_bug?bug_id=6511002

Did a bit of debugging out of curiosity, I think things are becoming unstuck in java.lang.ProcessImpl (see the constructor). Noticed that when it got to actually calling the underlying Windows API the string had turned into

explorer.exe "/select,"c:\New Folder\test.txt""

So that might explain why, as for workarounds see the bug database link.

Idolist answered 14/7, 2011 at 2:42 Comment(2)
That's not a bug: it is an RFE (Request for Enhancement). Specifically it is a request to document the existing behaviour.Turley
Thank you prunge! i did some debugging as well and reached the exact same conclusion with you, the multiple spaces get trimmed (by the tokenizer on ' ' i guess) and you end up with a single space instead. No matter if you use the string or the array version of exec. A breakpoint at line 452 of the java.lang.ProcessBuilder reveals that. I will take a look at the bug you mentioned and post any findings. Thnx very very very muchGodderd
U
0

For your specific case of needing the reveal/select command, I get around the windows quote nightmare by using cmd /c start:

String[] cmd = {"cmd", "/c", "start explorer.exe /select," + path};

Where path is the absolute path from a File object.

Utrillo answered 15/8, 2017 at 17:38 Comment(0)
N
0

A better way to do it would be using ProcessBuilder object:

 Process p;
 p = new ProcessBuilder("/Applications/Sublime Text.app/Contents/MacOS/sublime_text", homeDir + _CURL_POST_PUT_CMDS).start();
 int exitValue = p.waitFor();
 if (exitValue != 0){
    System.out.println("Error to open " + homeDir + _CURL_POST_PUT_CMDS);
 }
Novak answered 30/12, 2021 at 6:56 Comment(0)
M
-1

Simple way to resolve this problem for files is java.awt.Desktop Since 1.6 Example:

   Desktop.getDesktop().open(new File(fullFileName));
Monoclinous answered 11/11, 2013 at 13:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.