Bash variable expansion causes Java commandline to break - java

I've been experiencing a strange issue the last couple of days while writing a shell script which executes Java (starting the JBoss AS). I have a variable JAVA_OPTS that I am creating, and finally passing to the 'java' command. When I hard code the values in JAVA_OPTS rather than using variable expansion, the java process executes normally. When I use variable expansion, I get errors from the java executable. Here is the relevant portions of the script:
SERVER="-server"
MEM_OPTS="-Xms512m -Xmx1024m"
case "$1" in
start)
java "$SERVER" "$MEM_OPTS" $JAVA_OPTS \
-classpath "${JBOSS_CLASSPATH}" \
-Dorg.jboss.resolver.warning=true \
-Dsun.rmi.dgc.client.gcInterval=3600000 \
-Dsun.rmi.dgc.server.gcInterval=3600000 \
-Djboss.server.name=${SERVICE_NAME} \
-Djboss.server.base.dir=`dirname ${EC_APP_HOME}` \
-Djboss.server.base.url=file://`dirname ${EC_APP_HOME}` \
-Djboss.server.home.dir=${EC_APP_HOME} \
-Djboss.server.home.url=file://${EC_APP_HOME} \
org.jboss.Main >> out.log 2>&1 &
Executing this gives the following:
Invalid initial heap size: -Xms512m -Xmx1024m
Could not create the Java virtual machine.
However when I remove the variable expansion like so:
java "$SERVER" -Xms512m -Xmx1024m $JAVA_OPTS \
java executes no problems. As an aside, when I include "-server" in the MEM_OPTS var, I get the error "unrecognized option".
So obviously, there is something up with the variable expansion. I've hexdump'd the script, and made sure there are no extra characters in the string, and verified that I'm using Unix line endings. I've also reproduced the problem on two different linux machines, albeit both running the same version of ubuntu (one 32bit, the other 64bit).
EDIT: I get the same result with all forms of variable substitution: $MEM_OPTS, ${MEM_OPTS}, "${MEM_OPTS}"
Any ideas?

When you use "$MEM_OPTS", you're passing -Xms512m -Xmx1024m as a single option.
Try this instead:
java "$SERVER" $MEM_OPTS $JAVA_OPTS \
A good guide to variable substitution and quoting with examples: http://tldp.org/LDP/abs/html/varsubn.html

I am not 100% sure about the semantics of the quotes, but "$MEM_OPTS" may create a single argument "-Xms512m -Xmx1024m", whereas the JVM needs to see two seperate arguments. Can you try without the quotes?
java $SERVER $MEM_OPTS $JAVA_OPTS

think it should work if the quotes around $MEM_OPTS are removed, as the quotes tell bash to pass the expanded contents as a single token argument to execv()

My mistake - I was setting IFS to "\n" only in a script I sourced in, functions.sh. I was never resetting the IFS to "\n\t ", so the variables were not split on spaces.
Thanks for the responses!

It should be a simple matter of changing:
java "$SERVER" "$MEM_OPTS" $JAVA_OPTS \
to:
java "$SERVER" $MEM_OPTS $JAVA_OPTS \
so that the memory options are passed as two arguments.
But, since you say that doesn't work (?), please try the following.
A) Create a shell script fakejava.sh containing:
#!/usr/bin/bash
echo $#
while [[ $# -ne 0 ]] ; do
echo " [$1]"
done
(you may need to alter that first line if your bash is somewhere else: use which bash to figure out where).
B) Set its permissions correctly:
chmod 700 fakejava.sh
C) Replace your entire java command with:
./fakejava.sh "$SERVER" $MEM_OPTS $JAVA_OPTS xx
and see how many arguments there actually are. I get:
5
[-server]
[-arg1]
[-arg2]
[none]
[xx]
from the controlling script:
#!/usr/bin/bash
JAVA_OPTS="none"
SERVER="-server"
MEM_OPTS="-arg1 -arg2"
./fakejava.sh "$SERVER" $MEM_OPTS $JAVA_OPTS xx

Related

how to convert this .bat (windows) to .sh (linux-Centos)

I have the following code within .bat and it works fine on Windows
* #java -classpath ..\QVDReader.jar;..\lib\opencsv-2.3.jar;..\lib\jdbm-3.0-SNAPSHOT.jar -Dfile.encoding=UTF-8 ExampleQVDReader .\QVD\Customer.qvd .\CSV\Customer.csv ";" pause *
Summary, it's a library, that convert QVD file to CSV (without problem). But when I want to try it in a Centos Server, in terminal it's wrong, and I don't know why, I used this
java -classpath ../QVDReader.jar;../lib/opencsv-2.3.jar;../lib/jdbm-3.0-SNAPSHOT.jar -Dfile.encoding=UTF-8 ExampleQVDReader ./QVD/Customer.qvd ./CSV/Customer.csv ";"
I need execute it the same in Linux, any ideas?
Greetings.
You don't mention what shell you're using, but I'll assume it's an sh variant, in which semicolons ; are special characters that separate commands. So:
java -classpath ../QVDReader.jar;../lib/opencsv-2.3.jar;../lib/jdbm-3.0-SNAPSHOT.jar -Dfile.encoding=UTF-8 ExampleQVDReader ./QVD/Customer.qvd ./CSV/Customer.csv ";"
Is broken up into multiple commands at each semicolon, to this:
java -classpath ../QVDReader.jar
../lib/opencsv-2.3.jar
../lib/jdbm-3.0-SNAPSHOT.jar -Dfile.encoding=UTF-8 ExampleQVDReader ./QVD/Customer.qvd ./CSV/Customer.csv ";"
To treat the semicolons literally, put the argument in single quotes:
java -classpath '../QVDReader.jar;../lib/opencsv-2.3.jar;../lib/jdbm-3.0-SNAPSHOT.jar' -Dfile.encoding=UTF-8 ExampleQVDReader ./QVD/Customer.qvd ./CSV/Customer.csv ';'
You can use double-quotes too, but be aware of the differences.

Passing multiple positional shell arguments to java application

I programmed a Java app. For complex reasons I cannot export it as an executable (due to CVS and environment promoting practices) to Linux. I also cannot add the main class path to the MANIFEST.MF via 'jar -cvmf' command because it is not installed in the Linux environment the app is running in (I have no control over what gets installed). The only other option I found was to create the following shell script:
#!/bin/bash
#check that parameters were passed
if [ $# -lt 2 ]; then
echo ""
echo "Not enough arguments provided. You must have at least 2 arguments with ISO SQL time stamps."
echo " After that you can have unlimited number of parameters for tools."
echo ""
exit 1
fi
echo "Recovering events that occurred between $#"
ROOT_DIR=_spool_generator
JAR_DIR=jar
mkdir $ROOT_DIR
mkdir $ROOT_DIR/$JAR_DIR
FULL_DIR=$ROOT_DIR/$JAR_DIR
cp /home/wma/jar/SpoolGenerator.jar ./$FULL_DIR/
echo $#
START=$1
END=$2
java -cp "./$FULL_DIR/SpoolGenerator.jar" com.btv.main.Driver $# # --> does not work
#java -cp "./$FULL_DIR/SpoolGenerator.jar" com.btv.main.Driver "2001-02-12 18:15:00.0" "2001-02-12 19:15:00.0" --> works
#java -cp "./$FULL_DIR/SpoolGenerator.jar" com.btv.main.Driver "$START" "$END" --> works
echo "Execution is complete..."
exit
The key issue here is that I have an unlimited number of parameters the application uses. This works great when deploying the java app directly as an executable in Windows, works fine if I specify the positional arguments the Shell script takes, but how do I pass these same arguments to the java app from within the Linux script. I have to pass the parameters to the script surrounded in quotes due to the timstamp's special characters, this seems to cause some aberrant parsing when the parameters are passed to the jar. I appreciate any help.

Solaris/UNIX passing "$*" to compiled java

I am having a strange issue with passing "$*" to a java compiled program. The program will not parse the variables when I pass it from the following command line:
/export/home/checkout>/tmp/jsnmp.sh -f noc2 -t 4,4 -x \"resdiag SilentDiag 1\",18
The "/tmp/jsnmp.sh" contains the following:
#!/bin/sh
$JAVA_HOME/bin/java -jar /export/home/checkout/jsnmp.jar $*
Now if I run this:
$JAVA_HOME/bin/java -jar /export/home/checkout/jsnmp.jar \
-f noc2 -t 4,4 -x "resdiag SilentDiag 1",18
Everything works.
Any ideas folks?
You probably want to maintain the quoting within the script, so use "$#".
This has nothing to do with Java or Solaris, this is purely shell stuff.
This is because after $* substitution arguments will get re-parsed and will become separate arguments. E.g. your java executable will see it as
-f noc2 -t 4,4 -x resdiag SilentDiag 1,18
or something like that.
Check out the following test code:
a.sh:
echo $1
echo $2
echo $3
./b.sh $*
b.sh:
echo b
echo $1
echo $2
echo $3
Running it will produce the following output:
$ ./a.sh "1 2" 3
1 2
3
b
1
2
3
See how it was 2 arguments for the first script and 3 params for the second.
Encompassing $* in double quotes won't help, because it will send all arguments as one argument.
The following should work:
#!/bin/sh
$JAVA_HOME/bin/java -jar /export/home/checkout/jsnmp.jar "$1" "$2" "$3" "$4" "$5" "$6" "$7" "$8" "$9"
You will have some max number of arguments though...

powershell run java process problem

I'm trying to run a java process via Powershell in Windows XP. Here's the command:
java.exe -cp .;./common.jar -Dcontext=atest1 -Dresourcepath=. DW_Install
So, the classpath is . and .\common.jar (I think java takes the wrong slashes, right?) There are two environment variables, one "atest1" the other "." and the class to execute main on is DW_Install (in the default package).
This command works in cmd.exe, but doesn't is PS. What's going on? What is PS doing while parsing this command that CMD doesn't do (or vice versa)?
Aaron
The problem is that PS for some reason parses -Dresourcepath=. differently than cmd. What works is
java -cp '.;.\common.jar' -Dcontext=atest1 "-Dresourcepath=." DW_Install
It doesn't matter which way the slash goes, and it doesn't matter which quotes one uses (' or "). The classpath must be escaped, however, with some kind of quotes. A good test to see what's getting by the PS interpreter is to echo it. The following:
echo java -cp '.;.\common.jar' -Dcontext=atest1 -Dresourcepath=. DW_Install
yields the following output:
java
-cp
.;.\common.jar
-Dcontext=atest1
-Dresourcepath=
.
DW_Install
(Notice the resourcepath and the value of resourcepath are not on the same line.) Whereas the output to
echo java -cp '.;.\common.jar' -Dcontext=atest1 '-Dresourcepath=.' DW_Install
yields the following output:
java
-cp
.;.\common.jar
-Dcontext=etaste1
-Dresourcepath=.
DW_Install
Which is much more to our liking.
Although I wish this upon none of you, I hope that this post helps those of you that must deploy java projects on Windows machines (even though they will not run on any other platform ever).
Running external command-line programs from PowerShell is sometimes a bit problematic because there PowerShell exposes two different parsing modes that get trumped by the different syntaxes of said external programs.
In any case, running a command in Powershell requires using either the . prefix (dot-"sourcing") or the & operator.
You can workaround this by passing each parameter to the external program as separate variables, like so:
PS> $classpath = ".;./common.jar"
PS> $env = "-Dcontext=atest1 -Dresourcepath=."
PS> $class = "DW_Install"
PS> . java.exe -cp $classpath $env $class
Another example based on https://gaming.stackexchange.com/questions/24543/how-do-i-change-player-name-in-minecraft-multiplayer-in-offline-mode-in-linux
function mineCraftAs {
Param (
[parameter(mandatory=$true, HelpMessage="Minecraft character name." ,ValueFromPipeline=$true)]
[string] $name
)
if(!(test-path $env:appdata)) { $(throw "Appdata not found at $env:appdata")}
$private:minecraftPath=Join-Path $env:appdata .minecraft
if(!(test-path $minecraftPath)) { $(throw "Minecraft not found at $minecraftpath")}
$private:minebinPath=join-path $minecraftPath "bin"
if(!(test-path $minebinPath)) { $(throw "Minecraft bin not found at $minebinPath")}
$minebinPath | write-debug
gci $minebinpath | write-debug
#java -Xms512m -Xmx1024m -cp "%APPDATA%/.minecraft\bin\*" -Djava.library.path="%APPDATA%\.minecraft\bin\natives" net.minecraft.client.Minecraft '"'%1'"'
echo java -Xms512m -Xmx1024m -cp ('"'+$minebinPath+'\*"') ('-Djava.library.path="'+$minebinPath+'\natives"') net.minecraft.client.Minecraft ($name)
$minecraftJob=& 'C:\Program Files (x86)\Java\jre6\bin\java.exe' -Xms512m -Xmx1024m -cp ('"'+$minebinPath+'\*"') ('-Djava.library.path="'+$minebinPath+'\natives"') net.minecraft.client.Minecraft ($name)
}
minecraftas newbie
The following should work:
java.exe -cp '.;./common.jar' -Dcontext=atest1 -Dresourcepath=. DW_Install
I guess that PowerShell interprets the ; in the classpath as command delimiter, thereby trying to run java -cp . and ./common.jar -D....
start-process -nnw java "-cp .;./common.jar -Dcontext=atest1 -Dresourcepath=. DW_Install"

Calling java program dependent on external library

I am trying to call a java program in php to use it with web interface.
Java program is dependent on an external lib: commons-cli-1.2.jar
So basically I need to export it before calling the java program; but if I export it first as:
shell_exec('export CLASSPATH=$CLASSPATH:~/lib/commons-cli-1.2.jar');
then call the java program as:
shell_exec('java ComputePagerank -i $para_i -d $para_d -e $para_e -o $para_o');
I think it creates different shells for each call; then the export does not have any effect on java program. Or am I wrong?
Otherwise, it should output a file in the server. But simply it does not. So, what is wrong? Any idea?
edit: However can it be because some parameters such as para_i stands for an input file name, so that i have to specify full path for that? Because I just assume if the input file is in the same working directory, there won't be any problem, will it?
edit-2: it outputs properly when i use command line;)
you're right, each shell_exec creates a separate shell.
env CLASSPATH=whatever java -switches
I would use
shell_exec('java -cp $CLASSPATH:/home/yourname/dir/lib/commons-cli-1.2.jar ComputePagerank -i $para_i -d $para_d -e $para_e -o $para_o > message');
and (this is important) replace the tilde(~) with the actual path to your directory (/home/yourname say). The ~ is expanded by the shell and is dependent on which shell you''re using.
Try Creating a simple shell script with the commands that you want to execute. You may pass arguments to a shell script so that is not a problem either.
for example
echo "Running Script..."
java -cp $CLASSPATH:~/lib/commons-cli-1.2.jar ComputePagerank -i $1 -d $2 -e $3 -o $4 > message
etc.
Then try calling it from the command line first with some parameters. Did it output? Then try calling it from the php script. Did it output? If it did not then you may need to check permissions. I had a simiolar experience some time ago with a Java program that simply did not have permission to write a file.
You should be able to call it like this.
shell_exec('java -cp $CLASSPATH:~/lib/commons-cli-1.2.jar ComputePagerank -i $para_i -d $para_d -e $para_e -o $para_o > message');
Another option is to issue the 2 commands seperately, but to the same shell, like this:
shell_exec('export CLASSPATH=$CLASSPATH:~/lib/commons-cli-1.2.jar; java ComputePagerank -i $para_i -d $para_d -e $para_e -o $para_o > message');
edit:
some shells don't let you call export while you're setting up the variable. so this may be safer than the second option above:
shell_exec('CLASSPATH=$CLASSPATH:~/lib/commons-cli-1.2.jar; export CLASSPATH; java ComputePagerank -i $para_i -d $para_d -e $para_e -o $para_o > message');
another edit:
If none of the above work then you're going to have to do some more trouble shooting. Does your java program work from the command prompt?
java -cp $CLASSPATH:/home/user/lib/commons-cli-1.2.jar ComputePagerank -i param1 -d param2 -e param3 -o param4 > message

Categories

Resources