apache.commons.cli and "long" command-line parameters in Java - java

Is it possible to use 'long' parameters for command-line options with apache.commons.cli.CommandLineParser in java? 'Long' I mean not a single word, but a sentence in block-quotes ('"'). Java application is being started from bash-script file.
Script file:
#!/bin/bash
java -cp <classpath> MyClass $#
Call (which returns only "long" for '-p' argument and "another" for '-r' argument):
./script.sh -p "long parameter" -r "another long parameter"
Now I can only do something like this to get "long parameter" string:
./script.sh -p "long" -p "parameter"
Of course I can add '-p' programmatically as many times as needed, but this prevents me from using '-' sign in parameter values, as I need to track other command-line switches. Besides this seems to be far not correct approach.
CommandLine options are created like this:
org.apache.commons.cli.Options options = new Options();
options.addOption(Option.builder("p").longOpt("param").hasArg().desc("parameter description").build());
And then parsed like this:
CommandLineParser parser = new DefaultParser();
CommandLine line = parser.parse( options, args );
if (line.hasOption('p')) params = line.getOptionValues("param");

Finally it's got clear (thanks to Jim Garrison) that the problem is caused by calling java class from bash-script (passing all the arguments with $#). Wrapping $# into double quotes in script solved the problem. Like this:
#!/bin/bash
java -cp <path> MyClass "$#"
instead of incorrect
#!/bin/bash
java -cp <path> MyClass $#

Related

Execute bash script and pass arguments with spaces from java

I know there are a lot of post about executing commands from Java but I just can't get this to work. Here is what I'm trying to do, I have a bash script, it receives 2 arguments which might or might not have spaces, then from Java I'm executing the script and passing the arguments like this(I'm surrounding the arguments with quotes and escaping them with backslashes):
String cmd = "/opt/myScript \"/opt/myPath1\" \"/opt/myPath2 with spaces\"";
Runtime rt = Runtime.getRuntime();
rt.exec(cmd);
I also tried to use the ProcessBuilder class like this:
String myScript = "/opt/myScript";
String myArg1= "/opt/myPath1";
String myArg2 = "/opt/myPath2 with spaces";
ProcessBuilder pb = new ProcessBuilder(myScript , myArg1, myArg2);
pb.start;
Arguments with no spaces are received successfully but I still have problems with the second one.
I thought the ProcessBuilder class would handle the spaces but seems like I'm missing something.
I'm not sure if it has something to do, but just in case here is my script:
#!/bin/bash
PATH=$PATH:$1
gnome-terminal --working-directory $2
$1 and $2 are the arguments sent from Java.
Get the same trouble, finally solved with:
Runtime.getRuntime().exec(new String[]{"bash", "-c", <command with spaces>});
Runtime.exec() is an overloaded method. There are several possible ways how to call it. The call exec(String command) executes the specified string command but the argument are separated by spaces here. The method exec(String[] cmdarray) executes the specified command and arguments. There are other exec() variants but the best for you is
String cmd[] = new String[] {"/opt/myScript", "/opt/myPath1", "/opt/myPath2 with spaces" };
Runtime rt = Runtime.getRuntime();
rt.exec(cmd);
It is possible to use ProcessBuilder can be used as well for argument passing. I think the only error is missing parenthesis after pb.start.
And last but not least the script has a major bug. It does not contain quutes arround $2. It should be
#!/bin/bash
PATH="$PATH:$1"
gnome-terminal --working-directory "$2"

Pass shell script argument containing spaces as java system property

Have a shell script which, in turn, run a java program.
The script is invoked as follows :
./script.sh 1 2 3 4 "ab cd"
The 5th shell argument (ab cd) must be passed as a a java system property, what I'm doing is this :
JAVA_OPTS="-Xmx512M -Dlog4j.defaultInitOverride=true"
if [ "$5" ] ; then
JAVA_OPTS="$JAVA_OPTS -Dconfig.path=$5"
fi
Then, run java (JAVA_EXE & CP have proper values) :
$JAVA_EXE $JAVA_OPTS -classpath $CP com.foo.Main
Receiving this error :
Error: Could not find or load main class cd
If passing "abcd" instead of "ab cd" everything is ok.
If passing inline, just surround the value with quotes :
java -Xmx512M -Dconfig.path="ab cd" com.foo.Main
The problem occurs when a variable must be used.
How should I pass the argument containing spaces correctly ?
Instead of building JAVA_OPTS as a string, you can build it as an array:
JAVA_OPTS=(-Xmx512M -Dlog4j.defaultInitOverride=true)
if [ "$5" ] ; then
JAVA_OPTS+=("-Dconfig.path=$5")
fi
"$JAVA_EXE" "${JAVA_OPTS[#]}" -classpath "$CP" com.foo.Main
(Note: the Bourne shell did not have arrays, and POSIX does not require shells to support them, so this approach is not maximally portable. If you use this approach, make sure the first line of your script is something like #!/bin/bash or #!/bin/zsh and not something like #!/bin/sh.)
The only solution I found is to use a special variable for the problematic param
CONFIG_PATH="-Da=a"
if [ "$5" ] ; then
CONFIG_PATH=-Dconfig.path=$5
fi
$JAVA_EXE $JAVA_OPTS "$CONFIG_PATH" -classpath $CP com.foo.Main
It must have some value, otherwise the empty value will be taken as the name of the main class.
$JAVA_EXE $JAVA_OPTS -classpath $CP com.foo.Main MUST be $JAVA_EXE "$JAVA_OPTS" -classpath $CP com.foo.Main - Pay attention to the double quotes around $CP
EDIT: JAVA_OPTS="$JAVA_OPTS -Dconfig.path=$5" should also be JAVA_OPTS="$JAVA_OPTS -Dconfig.path='"'$5'"'"

Script to change strings in several files

I would like from my current folder containing Java files, to replace:
ColorTee.testh = new TestH(A,"JavaFileName".class);
ColorTee.testh.addFile(C,"/folder1/folder2/folder3/file","myFile")
with:
ColorTee.testh = new TestH(value1,A,"JavaFileName".class);
ColorTee.testh.addFile(C,"/folder3/file","myFile")
For the second line I would like just to remove /folder1/folder2
You you should do like :
String path = "/folder1/folder2/folder3/file";
ColorTee.testh.addFile(C,path,"myFile")
ColorTee.testh.addFile(C,path.replace("/folder1/folder2", ""),"myFile");
You could use a Perl script. e.g. here's one I have in my zsh:
change () {
from=$1
shift
to=$1
shift
for file in $*
do
perl -i.bak -p -e "s{$from}{$to}g;" $file
echo "Changing $from to $to in $file"
done
}
and that leverages the shell filename globbing etc. e.g.
$ change From To **/*.java
An alternative is not to change your method invocations, but change how the method works, and strip the initial path components. That's not so nice, and is also strongly dependent upon the actual implementation.
Right from eclipse:
http://www.saltycrane.com/blog/2007/03/how-to-do-global-searchreplace-across/

Executing a shell script with an empty argument from Java

I have bash script that takes in a couple of arguments tests then and runs a command as shown below
call script => /bin/bash myscript arg1 arg2 arg3 arg4
comm called => command -a arg1 -b arg2 -c arg3 -d arg4
if a parameter is empty then that option is not called.
call script => /bin/bash myscript arg1 arg2 '' arg4
comm called => command -a arg1 -b arg2 -d arg4
I am able to achieve this by using following line in the script to test the arguments
if test "${arg3:-t}" != "t" ; then
This script works like a charm when called from prompt. even if I replace '' with "" for an empty argument it works fine.
This starts failing when I call this script from java using exec.
Process p = Runtime.getRuntime().exec("/bin/bash myscript arg1 arg2 '' arg4");
expected command => command -a arg1 -b arg2 -d arg4 (as in above example)
actual command => command -a arg1 -b arg2 -c '' -d arg4
I am not able to understand why this would be happening. Where is the problem? In the shell script or in the way it command is executed from java?
How can this be fixed?
The basic problem is that java doesn't do anything at all fancy to parse the string you give it and break it nicely into arguments.
The short answer is to use the String [] version of Runtime.exec:
Runtime.getRuntime().exec(
new String[] {"/bin/bash", "myscript", "arg1", "arg2", "", "arg4"});
If you have other places where the argument parsing is more convoluted than that, you can pass the parsing of the string off to bash, and do:
Runtime.getRuntime().exec(
new String[] {"/bin/bash", "-c", "/bin/bash myscript arg1 arg2 '' arg4"});
If you're converting something over that's doing a huge number of complicated redirects like 2>&1 or setting up a whole pipeline, you might need this bash -c trick.
EDIT:
To understand what's going on here, you have to realize that when user-space code tells the kernel "load this executable with these arguments and start a process based on that" (*), what's passed on to the kernel is an executable, an array of strings for arguments (the 0th/first element of this argument array is the name of the executable, except when doing something weird), and an array of strings for the environment.
What bash does when it sees this line:
/bin/bash myscript arg1 arg2 '' arg4
is think "Okay, /bin/bash isn't a builtin, so I'm executing something. Let's put together the argument array for the subprocess using my string parsing algorithms, which know about quotes and whatnot". Bash then determines that the arguments to pass the kernel for the new process are:
/bin/bash
myscript
arg1
arg2
(empty string)
arg4
Now bash has pretty complicated string processing algorithms that it applies here - it accepts two different kinds of quotes, it'll do expansion of $VAR when it happens outside strings or inside double quotes, it'll replace subcommands in backquotes with the output, etc.
Java doesn't do anything so sophisticated when you call the single string version of exec. It just creates a new StringTokenizer and uses that to break up the string you give it into arguments. That class doesn't know anything about quotes; it splits that string up into:
/bin/bash
myscript
arg1
arg2
'' (a string with two characters, both of which are the single quote)
arg4
Java then calls the String[] version of exec. (Well, one of them)
Notes for people picking nits with this:
(*) Yes, I'm deliberately eliding the difference between the system calls fork and execve. Pretend they're one call for the moment.
Try this:
ProcessBuilder pb = new ProcessBuilder("/bin/bash", "myscript", "arg1", "arg2", "", "arg4");
Process proc = pb.start();
How about something like this?
javaParams=""
[ -n "$1" ] && javaParams="-a '$1'"
[ -n "$2" ] && javaParams="$javaParams -b '$2'"
[ -n "$3" ] && javaParams="$javaParams -c '$3'"
[ -n "$4" ] && javaParams="$javaParams -d '$4'"
command $javaParams
The
[ -n "$1" ] && javaParams="-a $1"
is equivalent to:
if [ -n "$1" ]
then
javaParams="-a $1"
fi
The -n test sees if the provided string is zero length or not.
You shouldn't use Runtime.exec. Java 5 introduced a ProcessBuilder which you should use instead. It gives you better control over starting the process and setting the environment.
ProcessBuilder pb = new ProcessBuilder("/bin/bash", "myscript", "arg1", "arg2", "", "arg4");
pb.start();

getting ghostscript to take in files with spaces in their name (like something in "my documents")

I'm trying to run this command to use ghostscript (from java) but Whether with single quote ' or " or nothing at all I get an error
Error: /undefinedfilename in ('c:\\Documents)
gswin32c.exe -q -dNOPAUSE -sDEVICE=tiffgray -sOutputFile=C:\polter.tiff -r300 'c:\Documents and Settings\polter.pdf' -c quit
any ideas?
use the short names for folders. short names are aliases which are using the 8.3 format. you can find the short name of a folder by using the dir /x command on the command prompt. your path will then look something like this: c:\docume~1\polter.pdf
Break up the arguments yourself:
String[] cmd = {
"gswin32c.exe",
"-q", "-dNOPAUSE", "-sDEVICE=tiffgray",
"-sOutputFile=C:\polter.tiff", "-r300",
"c:\Documents and Settings\polter.pdf",
"-c", "quit"
};
Runtime.getRuntime().exec(cmd);

Categories