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:
Answer from Jayan on Stack OverflowJayan, 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
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
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.
You don't need to include spaces. The ProcessBuilder will deal with that for you. Just pass in your arguments one by one, without space:
ProcessBuilder pb = new ProcessBuilder(
dir + "library/crc",
"-s",
fileName,
addressRanges);
We need spaces between arguments in commandline because the commandline need to know which is the first argument, which is the second and so on. However when we use ProcessBuilder, we can pass an array to it, so we do not need to add those spaces to differentiate the arguments. The ProcessBuilder will directly pass the command array to the exec after some checking. For example,
private static final String JAVA_CMD = "java";
private static final String CP = "-cp";
private static final String CLASS_PATH = "../bin";
private static final String PROG = "yr12.m07.b.Test";
private static final String[] CMD_ARRAY = { JAVA_CMD, CP, CLASS_PATH, PROG };
ProcessBuilder processBuilder = new ProcessBuilder(CMD_ARRAY);
The above code will work perfectly.
Moreover, you can use
Runtime.getRuntime().exec("java -cp C:/testt Test");
But it is more convenient to use ProcessBuilder, one reason is that if our argument contains space we need to pass quote in Runtime.getRuntime().exec() like java -cp C:/testt \"argument with space\", but with ProcessBuilder we can get rid of it.
ProcessBuilder processBuilder = new ProcessBuilder("command", "The first argument", "TheSecondWithoutSpace");
I was not able to get ProcessBuilder to work with this command. I did get my intended result, however, by simply building the command string manually and passing it directly to Runtime().exec as shown:
def command = "msdeploy.exe -verb:sync -source:contentPath='\My\Folder with Space\Path' -dest:auto"
def proc = Runtime.getRuntime().exec(command)
proc.waitForProcessOutput(System.out, System.err)
if (proc.exitValue()) {
throw new Exception("Command failed with exit code: " + proc.exitValue())
}
return proc.exitValue()
My CLI is Cygwin when I'm obliged to work in Windows, in part because of problems like this. The equivalent command would be:
msdeploy.exe -verb:sync -source:contentPath="/cygdrive/c/My/Folder\ with\ Space/Path"
The file separator is a forward slash and the spaces must be escaped with a backslash. Takes the guess work out.