I need to build the following command using ProcessBuilder:
"C:\Program Files\USBDeview\USBDeview.exe" /enable "My USB Device"
I tried with the following code:
ArrayList<String> test = new ArrayList<String>();
test.add("\"C:\\Program Files\\USBDeview\\USBDeview.exe\"");
test.add("/enable \"My USB Device\"");
ProcessBuilder processBuilder = new ProcessBuilder(test);
processBuilder.start().waitFor();
However, this passes the following to the system (verified using Sysinternals Process Monitor)
"C:\Program Files\USBDeview\USBDeview.exe" "/enable "My USB Device""
Note the quote before /enable and the two quotes after Device. I need to get rid of those extra quotes because they make the invocation fail. Does anyone know how to do this?
Joachim is correct, but his answer is insufficient when your process expects unified arguments as below:
myProcess.exe /myParameter="my value"
As seen by stefan, ProcessBuilder will see spaces in your argument and wrap it in quotes, like this:
myProcess.exe "/myParameter="my value""
Breaking up the parameter values as Joachim recommends will result in a space between /myparameter= and "my value", which will not work for this type of parameter:
myProcess.exe /myParameter= "my value"
According to Sun, in their infinite wisdom, it is not a bug and double quotes can be escaped to achieve the desired behavior.
So to finally answer stefan's question, this is an alternative that SHOULD work, if the process you are calling does things correctly:
ArrayList<String> test = new ArrayList<String>();
test.add("\"C:\\Program Files\\USBDeview\\USBDeview.exe\"");
test.add("/enable \\\"My USB Device\\\"");
This should give you the command "C:\Program Files\USBDeview\USBDeview.exe" "/enable \"My USB Device\"", which may do the trick; YMMV.
As far as I understand, since ProcessBuilder has no idea how parameters are to be passed to the command, you'll need to pass the parameters separately to ProcessBuilder;
ArrayList<String> test = new ArrayList<String>();
test.add("\"C:\\Program Files\\USBDeview\\USBDeview.exe\"");
test.add("/enable");
test.add("\"My USB Device\"");
First, you need to split up the arguments yourself - ProcessBuilder doesn't do that for you - and second you don't need to put escaped quotes around the argument values.
ArrayList<String> test = new ArrayList<String>();
test.add("C:\\Program Files\\USBDeview\\USBDeview.exe");
test.add("/enable");
test.add("My USB Device");
The quotes are necessary on the command line in order to tell the cmd parser how to break up the words into arguments, but ProcessBuilder doesn't need them because it's already been given the arguments pre-split.
I wasn't able to get it to work in any of the above ways. I ended up writing the command to a separate script (with "\ " for each space) and writing that into a script file, then calling the script file.
Split the arguments and add it to the command list. The ProcessBuilder will append quotes to the argument if it contains space in it.
ArrayList<String> cmd= new ArrayList<String>();
cmd.add("C:\\Program Files\\USBDeview\\USBDeview.exe");
cmd.add("/enable");
cmd.add("My USB Device");
Related
Below is basically an MCVE of my full problem, which is much messier. What you need to know is that the following line runs when directly put in terminal:
java -classpath /path/to/weka.jar weka.filters.MultiFilter \
-F "weka.filters.unsupervised.attribute.ClusterMembership -I first" \
-i /path/to/in.arff
This is relatively straightforward. Basically, all I am doing is trying to cluster the data from in.arff using all of the default settings for the ClusterMembership filter, but I want to ignore the first attribute. I have the MultiFilter there because in my actual project, there are other filters, so I need this to stay. Like previously mentioned, this works fine. However, when I try to run the same line with ProcessBuilder, I get a "quote parse error", and it seems like the whole structure of nesting quotes breaks down. One way of demonstrating this is trying to get the following to work:
List<String> args = new ArrayList<String>();
args.add("java");
args.add("-cp");
args.add("/path/to/weka.jar");
args.add("weka.filters.MultiFilter");
args.add("-F");
args.add("\"weka.filters.unsupervised.attribute.ClusterMembership");
args.add("-I");
args.add("first\"");
args.add("-i");
args.add("/path/to/in.arff");
ProcessBuilder pb = new ProcessBuiler(args);
// ... Run the process below
At first glance, you might think this is identical to the above line (that's certainly what my naive self thought). In fact, if I just print args out with spaces in between each one, the resulting strings are identical and run perfectly if directly copy and pasted to the terminal. However, for whatever reason, the program won't work as I got the message (from Weka) Quote parse error. I tried googling and found this question about how ProcessBuilder adds extra quotes to the command line (this led me to try numerous combinations of escape sequences, all of which did not work), and read this article about how ProcessBuilder/Runtime.exec() work (I tried both ProcessBuilder and Runtime.exec(), and ultimately the same problem persisted), but couldn't find anything relevant to what I needed. Weka already had bad documentation, and then their Wikispace page went down a couple weeks ago due to Wikispaces shutting down, so I have found very little info on the Weka side.
My question then is this: Is there a way to get something like the second example I put above to run such that I can group arguments together for much larger commands? I understand it may require some funky escape sequences (or maybe not?), or perhaps something else I have not considered. Any help here is much appreciated.
Edit: I updated the question to hopefully give more insight into what my problem is.
You don't need to group arguments together. It doesn't even work, as you've already noted. Take a look what happens when I call my Java programm like this:
java -jar Test.jar -i -s "-t 500"
This is my "program":
public class Test {
public static void main(String[] args) {
for( String arg : args ) {
System.out.println(arg);
}
}
}
And this is the output:
-i
-s
-t 500
The quotes are not included in the arguments, they are used to group the arguments. So when you pass the arguments to the ProcessBuilder like you did, it is essentially like you'd written them with quotes on the command line and they are treated as a single argument, which confuses the parser.
The quotes are only necessary when you have nested components, e.g. FilteredClassifier. Maybe my answer on another Weka question can help you with those nested components. (I recently changed the links to their wiki to point to the Google cache until they established a new wiki.)
Since you didn't specify what case exactly caused you to think about grouping, you could try to get a working command line for Weka and then use that one as input for a program like mine. You can then see how you would need to pass them to a ProcessBuilder.
For your example I'd guess the following will work:
List<String> args = new ArrayList<String>();
args.add("java");
args.add("-cp");
args.add("/path/to/weka.jar");
args.add("weka.filters.MultiFilter");
args.add("-F");
args.add("weka.filters.unsupervised.attribute.ClusterMembership -I first");
args.add("-i");
args.add("/path/to/in.arff");
ProcessBuilder pb = new ProcessBuiler(args);
Additional details
What happens inside Weka is basically the following: The options from the arguments are first processed by weka.filters.Filter, then all non-general filter options are processed by weka.filters.MultiFilter, which contains the following code in setOptions(...):
filters = new Vector<Filter>();
while ((tmpStr = Utils.getOption("F", options)).length() != 0) {
options2 = Utils.splitOptions(tmpStr);
filter = options2[0];
options2[0] = "";
filters.add((Filter) Utils.forName(Filter.class, filter, options2));
}
Here, tmpStr is the value for the -F option and will be processed by Utils.splitOption(tmpStr) (source code). There, all the quoting and unquoting magic happens, so that the next component will receive an options array that looks just like it would look if it was a first-level component.
I have the following code which builds my java command and runs process builder.
String runCommand[] = {"java", ExerciseSelected + " " + Input};
runpb = new ProcessBuilder(runCommand);
Process runp = runpb.start();
Input is a string of input separated by spaces. Currently I have an input of 100 which I am passing to my java program.
When running this it returns "Could not find or load main class Exercise 100"
Now i looked at another StackOverflow article that explains how to create a java command call. The command call to processbuilder looks like this
java Exercise 100
If I go to my java application folder and run this same call to the same Exercise.class it works from the command prompt. But it won't work from ProcessBuilder.
I have tried to enclose 100 in quotes and that also did not work. Is it possible there is something I have missed in putting together this command?
You're effectively trying to run
java "Exercise 100"
You want two arguments: "Exercise" and "100", so you should have them as different elements in the array:
String[] runCommand = { "java", exerciseSelected, input };
Note that if input is actually "1 2 3" this will be equivalent to running:
java Exercise "1 2 3"
which still may not be what you want. You may want to split input by spaces first.
(I've adjusted the variable names and the location of the [] to be more idiomatic.)
Searched for a while now...
I don't want to use a special parser, just build-in methods.
The problem is I want to read options like -i=something in a simple way.
Then - in the script - I can call these options like args[i] or so.
Is there any way?
Thanks
EDIT: Example
Command Line: java scriptname -write="test"
Script: System.out.println(args[write]);
Output: test
If you want to you pass parameters as key-value pairs while invoking java program, you can do that using -D flag like this
java -Demail=test#gmail.com -DuserName="John Watson" MainProgam
If your value has spaces in it, enclose that in double quotes. Now
you can access these values as system properties in your code like this
String email = System.getProperty("email");
String name = System.getProperty("userName");
All the parameters passed after class name in java command are accessible in args[] of main method, you can access them only through indices not with key value pairs
In the following example, what is the difference between the two methods used to call "php hello.php" in Java? p1 vs p2?
String[] commands;
String command = "php hello.php";
commands[0] = "php";
commands[1] = "hello.php";
Process p1 = Runtime.getRuntime().exec(command);
Process p2 = Runtime.getRuntime().exec(commands);
Thanks!
In your particular scenario, none.
The multi-args version doesn't require the command line to be split up into arguments, and consequently it's provided for command-line arguments with spaces/tabs etc.
From the Javadoc for the first (convenience) method:
More precisely, the command string is broken into tokens using a
StringTokenizer created by the call new StringTokenizer(command) with
no further modification of the character categories
(it's actually the version taking the environment too, but will get delegated to)
I am trying to create a GUI using java swing. From there I have to run linux system commands. I tried using exec(). But the exec() function is unable to parse the string if it contains single quotes. The code which I have used is as follows-
Process p = Runtime.getRuntime().exec("cpabe-enc pub_key message.txt '( it_department or ( marketing and manager ) )'")
BufferedReader stdInput = new BufferedReader(new InputStreamReader(p.getInputStream()));
But I am getting error when I run the program as--syntax error at "'(".
The same command runs when I write
Process p = Runtime.getRuntime().exec("cpabe-enc pub_key message.txt default")
Please help. Thanks in advance for your help.
Split up the parameters into an array instead, one string for each argument, and use the exec-method that takes as String[] instead, that generally works better for arguments.
Somethign along the lines of:
Runtime.getRuntime().exec(new String[] {"cpabe-enc", "pub_key", "message.txt", "( it_department or ( marketing and manager ) )"});
or whatever what your exact parameters are.
Its because the runtime does not interpret the '(...)' as a single parameter like you intend.
Try using ProcessBuilder instead:
http://docs.oracle.com/javase/1.5.0/docs/api/java/lang/ProcessBuilder.html
I recently got this kind of problem solved. I was using javaFX to call shell scripts on button click .. which is very much similar to your swing application scenario...
Here are the links hope it might help you...
How to code in java to run unix shell script which use rSync internally in windows environment using cygwin?
Getting error in calling shell script in windows environment using java code and cygwin...!
Happy coding... :)