Space in Java command-line arguments - java

In my Java command-line arguments, any characters after space get ignored. For example,
java test.AskGetCampaignByName "Dummy books"
I get the first argument (args[0]) as "Dummy" only. Single quotes also do not help.
Is there a workaround/fix for this? Could it be because of my terminal settings?
My $TERM is xterm, and $LANG is "en_IN".

The arguments are handled by the shell (I assume you are using Bash under Linux?), so any terminal settings should not affect this.
Since you already have quoted the argument, it ought to work. The only possible explanation I can think of is if your java command is a wrapper script and messes up the escaping of the arguments when passing on to the real program. This is easy to do, or perhaps a bit hard to do correctly.
A correct wrapper script should pass all its arguments on as ${1+"$#"}, and any other version is most likely a bug with regards to being able to handle embedded spaces properly. This is not uncommon to do properly, however also any occurrences of $2 or similar are troublesome and must be written as "$2" (or possibly ${2+"$2"}) in order to handle embedded spaces properly, and this is sinned against a lot.
The reason for the not-so-intuitive syntax ${1+"$#"} is that the original $* joined all arguments as "$1 $2 $3 ..." which did not work for embedded spaces. Then "$#" was introduced that (correctly) expanded to "$1" "$2" "$3" ... for all parameters and if no parameters are given it should expand to nothing. Unfortunately some Unix vendor messed up and made "$#" expand to "" even in case of no arguments, and to work around this the clever (but not so readable) hack of writing ${1+"$#"} was invented, making "$#" only expand if parameter $1 is set (i.e. avoiding expansion in case of no arguments).
If my wrapper assumption is wrong you could try to debug with strace:
strace -o outfile -f -ff -F java test.AskGetCampaignByName "Dummy books"
and find out what arguments are passed to execve. Example from running "strace /bin/echo '1 2' 3":
execve("/bin/echo", ["/bin/echo", "1 2", "3"], [/* 93 vars */]) = 0
brk(0) = 0x2400000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f420075b000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f420075a000
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
open("/usr/lib64/alliance/lib/tls/x86_64/libc.so.6", O_RDONLY) = -1 ENOENT (No such file or directory)
stat("/usr/lib64/alliance/lib/tls/x86_64", 0x7fff08757cd0) = -1 ENOENT (No such file or directory)
open("/usr/lib64/alliance/lib/tls/libc.so.6", O_RDONLY) = -1 ENOENT (No such file or directory)
...

In case your program needs more than positional arguments (= when the command line usage is important), you should consider options and switches. Apache Commons has a great library for this.

You just have to escape the spaces like this:
normal String: "Hello World!"
escaped String: "Hello" "World!"
That worked for me.
My environment:
23:39:19 Zarathustra#thora:/Users/Zarathustra~$bash -version
GNU bash, version 3.2.48(1)-release (x86_64-apple-darwin11)
Copyright (C) 2007 Free Software Foundation, Inc.

It sounds like you are using a operating system distribution where the java command available to the user is a wrapper which finds the right JVM "somewhere" and invokes it accordingly.
If so, it most likely does not escape the arguments properly when invoking the actual java executable.
What distribution do you use?

Just reassemble the arguments in your Java program:
StringBuilder allArgs = new StringBuilder();
for (int i=0; i < args.length; i++)
{
//System.out.println("arg"+i+": "+args[i]);
allArgs.append(args[i]+" ");
}
// Parse out the args the way you wish using allArgs

Related

Losing java main args when using getopts in launch script

I have a script with 5 mandatory parameters (5 paths) and 3 options (-d for debug, -l for log4j override, -s for another override).
I'm managing it with getopts. The following script is simplified :
LOG4J_FILE=$DEFAULT_LOG4J_FILE
S_FILE=$DEFAULT_S_FILE
ECLIPSE_PROPS=
while getopts "l:s:d" flag; do
case "$flag" in
l) LOG4J_FILE="$OPTARG";;
s) S_FILE="$OPTARG";;
d) ECLIPSE_PROPS="-Xdebug ...";;
:) usage;;
?) usage;;
esac
done
shift $((OPTIND-1))
OPTIND=1
...
echo_and_eval $JAVA $ECLIPSE_PROPS -Dlog4.configuration=$LOG4J_FILE -Ds.file=$S_FILE -cp $CLASSPATH $MAIN_CLASS $ARGS
If I just put the 5 parameters, it works.
If I add one or two optional with parameters (l or s), it works.
If I add the -d option, I have no args in the Java main method.
Any clue ? This is driving me crazy.
It's OPTARG not OPTARGS -- http://www.gnu.org/software/bash/manual/bashref.html#index-getopts
l) LOG4J_FILE="$OPTARG";;
I would encourage you to get into the habit of quoting ALL your variables, that will protect any that contain whitespace or globbing chars:
java "$ECLIPSE_PROPS" -Dlog4.configuration="$LOG4J_FILE" -Ds.file="$S_FILE" -cp "$CLASSPATH" Main "$1" "$2" "$3" "$4" "$5"
Simply using echo to output the command line isn't going to show you how it gets parsed into individual arguments. foo 'bar baz' and foo bar baz are two very different commands, but they look the same when echoed.
Clearly, something in the actual value of the ECLIPSE_PROPS variable (that is, the argument to -d) is preventing the later arguments from being passed to the Java main method. If you supplied the actual code and actual values, we might be able to help you determine what that is.

Passing pre-escaped command-line arguments to ProcessBuilder

I bumped into this problem today when setting up a local set of communicating programs. Basically one of my applications is sending some data to another, and part of this data is a string containing a command to execute (like you would from the command-line). Let's say, for example:
g++ foo.cc bar.cc -o foobar
is the command sent by my first application. The second application, which receives the command (amongst other things), needs to execute this command after doing some other processing.
Now, at first I thought this would be trivial using a ProcessBuilder:
String exampleCommand = "g++ foo.cc bar.cc -o foobar";
ProcessBuilder builder = new ProcessBuilder(exampleCommand);
builder.start().waitFor();
However this is where the problem occurs.
CreateProcess error=2, The system cannot find the file specified
Okay, no worries I guess I can't just dump the whole thing into the builder. The first part of the command is usually a trivial string so I thought I could probably get away with a split around the first ' ' to separate the program name and arguments.
String exampleCommand = "g++ foo.cc bar.cc -o foobar";
String[] parts = exampleCommand.split(" ", 2);
ProcessBuilder builder = new ProcessBuilder(parts[0], parts[1]);
builder.start().waitFor();
And this brought me a little closer, the g++ file could now be found correctly, however after examining the stderr of g++ I found that the following error had occurred:
g++.exe: error: foo.cc bar.cc -o foobar: No such file or directory
At this point I realised that the ProcessBuilder class must be escaping all arguments passed to it in preparation for the command-line (hence the reason it usually takes arguments as an array of individual arguments rather than just a predefined argument string).
My question is, "Is there any way to pass a raw string of arguments to a ProcessBuilder and say THERE, execute EXACTLY this?"
Because the command comes from another application and is in no way static I can't just break the arguments down into an array beforehand and pass them to the ProcessBuilder constructor properly. The arguments are not so trivial that simply splitting the string around a ' ' will work properly either; arguments might contain spaces escaped with double quotes. For example:
g++ "..\my documents\foo.cpp" bar.cpp -o foobar
Could be a command coming from the application and splitting that string around ' ' and passing it to the ProcessBuilder will result in corrupt arguments.
If there is no proper way to do this can someone please point me to a standalone command line argument parser (in Java) that can turn a command-line string into a valid String[]?
Okay I feel rather foolish now but I achieved my desired result by simply reverting back to the good old Runtime.getRuntime().exec(...). I'll leave the question up in case anyone is as silly as me and find it useful.
String exampleCommand = "g++ foo.cc bar.cc -o foobar";
Runtime sys = Runtime.getRuntime();
sys.exec(exampleCommand);
Easy.
A comment to the Runtime.getRuntime().exec(...) solution:
The Runtime.getRuntime().exec(...) is not good anymore. In java executed on OSX El Capitan, 'Runtime.getRuntime().exec(...)' contains an error that sometimes closes the opened process when the java program exits. It works fine on previous OSX versions. However, ProcessBuilder works on all OSX versions.
(Haven't posted enough to have a enough rep points to make this as a normal comment.)

error while running CRF++ from a java project

I want to call CRF++ toolkit from a java program. I type the following:
Process process = runtime.exec("/home/toshiba/Bureau/CRF++-0.54/.libs/lt-crf_learn /home/toshiba/Bureau/CRF++-0.54/example/atb/template /home/toshiba/Bureau/CRF++-0.54/example/atb/tr_java.data");
process.waitFor();
But, I have the the following error:
CRF++: Yet Another CRF Tool Kit
Copyright (C) 2005-2009 Taku Kudo, All rights reserved.
Usage: /home/toshiba/Bureau/CRF++-0.54/.libs/lt-crf_learn [options] files
-f, --freq=INT use features that occuer no less than INT(default 1)
-m, --maxiter=INT set INT for max iterations in LBFGS routine(default 10k)
-c, --cost=FLOAT set FLOAT for cost parameter(default 1.0)
-e, --eta=FLOAT set FLOAT for termination criterion(default 0.0001)
-C, --convert convert text model to binary model
-t, --textmodel build also text model file for debugging
-a, --algorithm=(CRF|MIRA) select training algorithm
-p, --thread=INT number of threads(default 1)
-H, --shrinking-size=INT set INT for number of iterations variable needs to be optimal before considered for shrinking. (default 20)
-v, --version show the version and exit
-h, --help show this help and exit
I 'm wondering if any one could help me?
I don't think that's a bug in CRF++, since you are able to run it from command line. So the actual question is how to pass arguments properly when starting a process using Runtime.exec(). I would suggest trying the following:
String[] cmd = {"/home/toshiba/Bureau/CRF++-0.54/.libs/lt-crf_learn",
"/home/toshiba/Bureau/CRF++-0.54/example/atb/template",
"/home/toshiba/Bureau/CRF++-0.54/example/atb/tr_java.data"};
Process p = Runtime.getRuntime().exec(cmd);
This may help since Runtime.exec() sometimes splits the command line into arguments in a rather strange fashion.
Another potential problem is mentioned here: Java Runtime.exec()
There's a simple solution for this. Just write your command into a temporary file and execute that file as Runtime.getRuntime.exec("sh <temp-filename>"). Later you can delete this file. I will explain reason behind this if this solution works for you.

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/

Standard error not getting redirected from this java command in bash

I have the following shell script. For some reason the java program's standard error and standard output is not printed to the file "log" but instead always appear in the console. Is there a type some where or am I missing something?
JAVACMD="java -XX:+HeapDumpOnOutOfMemoryError -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=19000 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false -Xss128k -Xmx700m -jar program.jar >log 2>&1 "
echo "Starting server...";
$JAVACMD&
Regrettably, you will have to use eval if you want to keep the redirection operators in the string value.
Like other shell-special characters, redirection operators are only evaluated if they are not quoted. When you expand the JAVACMD parameter it will split on whitespace, but it will not re-evaluate any special characters it includes. Using eval forces this re-evaluation.
The problem with eval is it forces every character be re-evaluated. In your case, none of the other characters will have any untoward affects. If your string value contained some other shell-special character (e.g. ;(){}…) that you did not want the shell to re-evaluate you would have to escape/quote it inside the string value so that eval would not give it a special meaning.
⋮
eval "$JAVACMD &"
To avoid problems with eval, I suggest moving the redirection out of the string value:
JAVACMD="… program.jar"
⋮
$JAVACMD >log 2>&1 &
Done this way the only characters in the string value that you need to watch out for are the whitespace characters (e.g. if you needed some embedded whitespace in one of the options/arguments; if you run into this you might consider using an array variable or "$#" (a singular, array-like variable available in all Bourne-like shells)).
Have you tried:
JAVACMD="java -XX:+HeapDumpOnOutOfMemoryError -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=19000 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false -Xss128k -Xmx700m -jar program.jar"
echo "Starting server...";
$JAVACMD >log 2>&1 &
The redirections are considered as arguments to the java command as they are contained in the variable.
You can't actually stick redirections in a variable like that and expect bash to accept them. Simple example:
tesla:~ peter$ ECHOCMD='echo > log 2>&1'
tesla:~ peter$ $ECHOCMD
log 2>&1
I.e. your redirection indicators are becoming simple arguments, passed to your java invocation.
One workaround is to say eval $JAVACMD but it won't make your script any cleaner.

Categories