5

I'm using ProcessBuilder to run a Windows executable...the exact command I need to run is :

"C:\Program Files\CCBU\CCBU.exe" -d"C:\My Data\projects\ccbu\ciccb-report.xls" -tf"C:\Program Files\CCBU\loss-billing-filters.txt"

If I run the above command from a command prompt, it works fine.

If I then issue the command and arguments as indicated in the following StackOverflow post (ProcessBuilder adds extra quotes to command line) as a String [] array it fails, as the spaces in the directory paths break the arguments somehow to the CCBU.exe executable :

[log-snippet]
2015-08-31 10:39:08,937 [main] INFO  rpd.primary - C:\Program Files\CCBU\CCBU.exe
logging to the given report's directory
Configuration file is: ./CCBUConfigFile.txt
Running with the following settings:
Report Filepath:       C:\My
Search Terms FilePath: C:\Program

2015-08-31 10:39:08,948 [main] INFO  rpd.primary - STDERR:--------------------
2015-08-31 10:39:08,961 [main] INFO  rpd.primary - 
Warning: parameter Data\projects\ccbu\ciccb-report.xls not recognized. Ignoring

Warning: parameter Files\CCBU\loss-billing-filters.txt not recognized. Ignoring

Error: C:\Program not found or not readable
[/log-snippet]

If I move the data files and the filters to a directory path with no spaces this works fine :

"C:\Program Files\CCBU\CCBU.exe" -d"C:\Users\n0002501\ccbu\ciccb-report.xls" -tf"C:\Users\n0002501\ccbu\loss-billing-filters.txt" 

The issue is, the users of this process will be placing files in folders (directories) that DO have spaces. So somehow I have to get it working with spaces. I'm thinking it's something simple, but what am I missing?

I'm using the Classes from this posting to handle the Threads for STDOUT and STDERR : http://alvinalexander.com/java/java-exec-processbuilder-process-2

Here's the code :

            // Split the Arguments : 
            // In Eclipse and runtime, the arguments get broken : 
            // The STDOUT from the command shows the Report Filepath
            // and Search Teams FilePath as broken at the 1st space...
            // 
            // Report Filepath:       C:\My
            // Search Terms FilePath: C:\Program
            // 
            // SHOULD BE : 
            // 
            // Report Filepath:       C:\My Data\projects\ccbu\ciccb-report.xls
            // Search Terms FilePath: C:\Program Files\CCBU\loss-billing-filters.txt
            // 
            try { 
                commands.add ( "\"C:\\Program Files\\CCBU\\CCBU.exe\""                      );
                commands.add ( "-d\"C:\\My Data\\projects\\ccbu\\ciccb-report.xls\""        );
                commands.add ( "-tf\"C:\\Program Files\\CCBU\\loss-billing-filters.txt\""   );
                commandExecutor = new SystemCommandExecutor(commands);
                commandExecutor.setLog ( getLog() );

                // DEBUG : Build and printout the commands...
                // 
                lstrCommand = "";
                for ( int theIdx=0; theIdx<commands.size (); theIdx++ ) {
                    if ( theIdx == 0 ) { 
                        lstrCommand = lstrCommand + commands.get ( theIdx );
                    }
                    else { 
                        lstrCommand = lstrCommand + " " + commands.get ( theIdx );
                    }
                    getLog().debug ( SHORT_NAME + " Building Command[] [" + commands.get ( theIdx ) + "]" );
                }

                getLog().debug ( SHORT_NAME + " Running Command[] [" + lstrCommand + "]" );

                result = commandExecutor.executeCommand();

                // get the stdout and stderr from the command that was run
                stdout = commandExecutor.getStandardOutputFromCommand();
                stderr = commandExecutor.getStandardErrorFromCommand();

                // print the stdout and stderr
                getLog().info ( "SystemCommandExecutor - Status Code [" + result + "]" );
                getLog().info ( "STDOUT:--------------------" );
                getLog().info( stdout );
                getLog().info ( "STDERR:--------------------" );
                getLog().info( stderr );
            }
            catch ( Exception ltheXcp ) { 
                getLog().error ( SHORT_NAME + ".runTask () - Error/exception on commands [3-spaces] [" + lstrCommand + "]" );
            }
            finally { 
                commands.clear ();
                stdout = null;
                stderr = null;
                commandExecutor = null;
            }

Jayan, The final code that works :

            try { 
                commands.add ( "C:\\Program Files\\CCBU\\CCBU.exe"                      );
                commands.add ( "-dC:\\My Data\\projects\\ccbu\\ciccb-report.xls"        );
                commands.add ( "-tfC:\\Program Files\\CCBU\\loss-billing-filters.txt"   );

                commandExecutor = new SystemCommandExecutor ( commands );
                commandExecutor.setLog ( getLog() );

All I had to do is take out all the double-quotes and let ProcessBuilder handle the directory paths on it's own...

tia, adym

2
  • no space between the flags and argumentss? looks funny Commented Aug 31, 2015 at 15:27
  • Yes, and the CCBU utility will NOT let me put a space there...it's some kinda Python utility fronted by a *.exe Commented Aug 31, 2015 at 15:30

2 Answers 2

6

Add individual strings without "double" quotes..

                commands.add ( "C:\\Program Files\\CCBU\\CCBU.exe"                      );
                commands.add ( "-d");
                commands.add ("C:\\My Data\\projects\\ccbu\\ciccb-report.xls"        );
                commands.add ( "-tf");
                commands.add("C:\\Program Files\\CCBU\\loss-billing-filters.txt"   );
                commandExecutor = new SystemCommandExecutor(commands);

ProcessBuilder will take care of necessary handling of args.


Pull up comment:

Jayan, You're idea got me thinking : The following worked :

 commands.add ( "-dC:\\My Data\\projects\\ccbu\\ciccb-report.xls" );
 commands.add ( "-tfC:\\Program Files\\CCBU\\loss-billing-filters.txt"

); – lincolnadym

Sign up to request clarification or add additional context in comments.

3 Comments

Hi Jayan, You're correct, that allows it to correctly deal with the spaces, but what happens is the ProcessBuilder now puts a space between the -d and the "C:\My Data\projects\ccbu\ciccb-report.xls" and the -tf and "C:\Program Files\CCBU\loss-billing-filters.txt", which cause the CCBU utility to error out : Warning: parameter C:\My Data\projects\ccbu\ciccb-report.xls not recognized. Ignoring Warning: parameter C:\Program Files\CCBU\loss-billing-filters.txt not recognized. Ignoring
that is a strange utility.. Looks like -tf"C:\Users\n0002501\ccbu\loss-billing-filters.txt" must be single string: Try commands.add("-tf\"C:\Users\n0002501\ccbu\loss-billing-filters.txt")
Jayan, You're idea got me thinking : The following worked : commands.add ( "-dC:\\My Data\\projects\\ccbu\\ciccb-report.xls" ); commands.add ( "-tfC:\\Program Files\\CCBU\\loss-billing-filters.txt" );
0

An old question, but I was running into similar behavior and wanted to share some background on what I found for this for anyone else like me who is looking.

Lack of argument separation in OS: Windows OS programs only take a single argument - CmdLine. The system calls for launching programs (at least the only ones I found -- WinExec and CreateProcess) do not, like on Linux, accept an array of arguments, but instead a string of CmdLine. This means that the program and the caller need to agree on how individual arguments are encoded - the OS doesn't make the separation of arguments part of the contract.

CmdLine convention: It seems from my experiments, that powershell, python, and more, agree that to pass separate arguments in CmdLine, they should be joined with a space, and if they contain space or double quote, they should be wrapped in double quotes with contained double quotes escaped by a backslash. This is good -- we can still pass any value as individual arguments.

ProcessBuilder flaw: ProcessBuilder accepts individual arguments. You would expect these to be passed to the subprocess as individual arguments in a way appropriate on the OS you are running. However, it seems it doesn't correctly do this when a double quote is within the argument on Windows. In my tests, it did not escape it. So if you have new ProcessBuilder("C:\\Program Files\\CCBU\\CCBU.exe", "-d\"my file\"", "-tf\"my other file\""), it ends up translating to a CmdLine value of "C:\Program Files\CCBU\CCBU.exe" "-d"my file"" "-tf"my other file"". At least dotnet seems to interpret this as having the arguments -dmy, file, -tfmy, other, file (i.e., values (quoted or not) next to other values (quoted or not) are treated as one argument, while spaces after even numbers of unescaped quotes are treated as separators.

Takeaway: Whenever you use ProcessBuilder with arguments containing double quotes, check if on Windows, and if so, add slashes before double quotes in each argument you pass.

Quotes in the middle of arguments: As you see in the above example, something like -d"hello world" can become a single value of -dhello world in some contexts, including CmdLine, bash, and Cmd. This is a mechanism to allow for spaces without having to quote the whole argument ("-dhello world" would act the same). However, in the ProcessBuilder context, there is no such handling of these quotes, and instead arguments are separated by being separate ProcessBuilder inputs. This means that the quote is treated as part of the argument (preserved as literal -d"hello world").

Takeaway: Only add double quotes in ProcessBuilder arguments if the program you are calling wants a value with double quotes in it. If they are there only to preserve whitespace during the invocation, omit them.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.