Writing a `jjs` shebanged script that accepts just arguments - java

Supposedly jjs is the Java 8 version of nashorn, ready for shell-scripting. However when I write a file with execute permission as:
#!/usr/bin/jjs
arguments.forEach(function(v,i,a){print(v);});
Running it produces some not so ideal shell-scripting behavior:
$./file.js
$./file.js one two
java.io.IOException: one is not a file
$./file.js -- one two
one
two
$./file.js file.js -- one two # what were they thinking
one
two
one
two
$
So, I don't ever want this script I'm writing to allow for the arbitrary interpretation of a file after it runs, maybe I should use -- in the shebang like:
#!/usr/bin/jjs --
arguments.forEach(function(v,i,a){print(v);});
But this gets less useful:
$./file.js
jjs>^D
$./file.js one two
jjs>^D
$./file.js -- one two
jjs>
Yes, being dropped into a REPL prompt is exactly what I thought should not happen.
How am I supposed to make it possible to execute a script with arguments that are passed directly and not interpreted by jjs itself such that I can get behavior like:
$./file.js one two
one
two
$

The example works as expected in the JDK 9 version of Nashorn. I'll take care of backporting the required patches so they can appear in one of the upcoming 8u releases.
Update: the required changes are backported to the 8u stream, but it is not clear at what point there will be a release. The early access releases of JDK 9 available from https://jdk9.java.net/download/ have the shebang feature, and also other extensions, such as piping support for the $EXEC builtin.

While waiting for the changes to be backported, I wrote a script to overcome the problem. My jvm is installed in the folder /usr/lib/jvm/jdk1.8.0_101, so I created the following script:
/usr/lib/jvm/jdk1.8.0_101/bin/jjs/bin/jjs-cp.sh:
#!/bin/bash
CP=
if [[ $1 = "--lib="* ]]; then
LIB_FOLDERS=${1:6}
shift
LIB_FOLDERS=$(echo $LIB_FOLDERS | tr ":" "\n")
for FOLDER in $LIB_FOLDERS; do
if [[ $FOLDER = "!"* ]]; then
CP="$CP:${FOLDER:1}"
else
if [ -d $FOLDER ]; then
JARS=$(find "$FOLDER" -type l -o -type f -name "*.jar")
for JAR in $JARS; do
CP="$CP:$JAR"
done
fi
fi
done
if [ -n $CP ]; then
CP=${CP:1}
fi
fi
SCRIPT_FILE="$1"
shift
if [ -n $CP ]; then
/usr/lib/jvm/jdk1.8.0_101/bin/jjs -cp "$CP" $SCRIPT_FILE -- $#
else
/usr/lib/jvm/jdk1.8.0_101/bin/jjs $SCRIPT_FILE -- $#
fi
Then, in my test.js script I can write:
#!/usr/lib/jvm/jdk1.8.0_101/bin/jjs-cp.sh --lib=!.:<full path to classpath folder 1>:<full path to classpath folder 2>
var console = {
log: function(msg) {
java.lang.System.out.println(msg);
}
};
console.log('Hello, world!');
arguments.forEach(console.log);
var MyClass = Java.type('com.acme.MyClass'); // load a class from a jar in the classpath
console.log(MyClass.class); // prints "class com.acme.MyClass"
And I can execute my script with this command line:
~$ ./test.js one two

Related

create shell script for parameterized variable [duplicate]

This question already has answers here:
How to execute a bash command stored as a string with quotes and asterisk [duplicate]
(5 answers)
Closed 3 years ago.
I am very much new to shell script and i want to create and run shell script.
I want to run my java program in two different environment which is simu and prod.
If i am running the program in the simu environment then execute SIMU_RUN else PROD_RUN.
For example i have two directory where file is placed in /data/etl-simu/in/ and /data/etl-prod/in/ and as you can see while reading the file from the directory name i can recognise whether the environment is simu or prod from SIMU_PATH or PROD_PATH variable.
I am not sure if it easy to write such shell script and execute it.
If i just create normal shell script and put the complete SIMU_RUN or PROD_RUN path in that shell script and execute it then in the respective environment then it will run fine.
But as i have two environment then i want to make this shell script flexible instead of creating two separate shell script for simu and prod
#!/bin/sh
SIMU_RUN="cd /opt/env/import/import/current; java -Dlog4j.configurationFile=/opt/import/config/logging/log4j2_Importer.xml -Djava.security.egd=file:///dev/urandom -classpath /opt/runner/lib/*:/opt/import/lib/* runner.Application --config /opt/import/config/import.simu.properties --workflow import --inputDir /data/etl-simu/in"
PROD_RUN="cd /opt/import/import/current; java -Dlog4j.configurationFile=/opt/import/config/logging/log4j2_Importer.xml -Djava.security.egd=file:///dev/urandom -classpath /opt/runner/lib/*:/opt/import/lib/* runner.Application --config /opt/import/config/import.prod.properties --workflow import --inputDir /data/etl-prod/in"
SIMU_PATH="/data/etl-simu/in"
PROD_PATH="/data/etl-prod/in"
MODE=$1
if [ "${MODE}" = SIMU_PATH ]; then
#execute SIMU_RUN
else
#execute PROD_RUN
fi
exit ${EXIT}
Don't store code in a variable, use a function:
#!/bin/sh
run() {
cd /opt/import/import/current &&
java -Dlog4j.configurationFile=/opt/import/config/logging/log4j2_Importer.xml \
-Djava.security.egd=file:///dev/urandom \
-classpath /opt/runner/lib/*:/opt/import/lib/* \
runner.Application \
--config "/opt/import/config/import.${mode}.properties" \
--workflow import \
--inputDir "/data/etl-${mode}/in"
}
mode=$1
case "$mode" in
prod|simu)
run
;;
*) echo "error: invalid mode" >&2
exit 1
;;
esac
Notes:
Get out of the habit of using ALLCAPS variable names, leave those as reserved by the shell. One day you'll write PATH=something and then wonder why your script is broken.
I've broken up the very long lines with line continuations: that will make maintainability much easier.
If you make this a bash script, then it's even nicer IMO:
run() {
local -a java_options=(
-Dlog4j.configurationFile=/opt/import/config/logging/log4j2_Importer.xml
-Djava.security.egd=file:///dev/urandom
-classpath "/opt/runner/lib/*:/opt/import/lib/*"
)
local app="runner.Application"
local -a app_options=(
--config "/opt/import/config/import.${mode}.properties"
--workflow import
--inputDir "/data/etl-${mode}/in"
)
cd /opt/import/import/current &&
java "${java_options[#]}" $app "${app_options[#]}"
}

How do I run a Command Line script from PowerShell? [duplicate]

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"

HOME environment variable not set in bash script

I have a Java program that spawns a bash script that calls another script. In that second script, I'm finding that the $HOME variable is not set. Here's the gist of it:
In Java:
Process p = new ProcessBuilder("bash", "-l", "/foo/a.sh").start();
// code to actually execute p
/foo/a.sh:
#!/bin/bash
/foo/b.sh
/foo/b.sh:
#!/bin/bash
echo "HOME=$HOME"
This echoes "HOME=". The eventual problem is that $HOME/bin is supposed to be added to my PATH in ~/.profile, but since that's not happening, a bunch of custom executables aren't being made accessible.
I worked around it by doing:
if [ -d ~/bin ] ; then
PATH=~/bin:"$PATH"
fi
And that works fine. But I guess I just want to understand why $HOME wasn't being set. It seems like $HOME and ~ should be largely equivalent, no? There's probably something I'm fundamentally missing about how this environment is getting set up.
I am running Ubuntu 12.04.5, if that makes a difference.
The evidence suggests that HOME is missing from the environment in which the Java app is running. Assuming that the app doesn't explicit unset HOME, the most likely reason is that the app is being started from some context other than a login by the user the app is running as.
It's correct that ~ and $HOME are similar. If HOME is present in the environment, even if it is set to the empty string, ~ will be replaced with $HOME. However, if HOME is not present in the environment, bash will attempt to find the home directory for the currently logged in user, and use that for ~.
eg.
$ bash -c 'echo ~'
/home/rici
$ HOME='Hello, world!' bash -c 'echo ~'
Hello, world!
$ HOME= bash -c 'echo ~'
$ (unset HOME; bash -c 'echo ~';)
/home/rici
Since your workaround requires the equivalent of the last scenario, I conclude that HOME has not been set, or has been unset.

Bash - Script to deploy and run jar files

im working on distributed systems and my program is complete, i have tested it and it runs fine over 10 machines, but every time i want to test the program i have to:
- Copy the file for each machine
- ssh to each machine and type "java -jar file"
To avoid that painfull process I made this
for i in {1..11}
do
if [ $i -ne 6 ];
then
sshpass -p "qwerty" scp myJar.jar user#l040101-ws$i.XXXX.XX.XX:someFolder;
sshpass -p "qwerty" ssh user#l040101-ws$i.XXXX.XX.XX "java -jar someFolder/myJar.jar &" &
fi
done
And for some reason it doesnt work like it should, the scp command executes as it should, but the other one doesnt.
The program should produce a folder with 2 logs inside and if i do it manually it does, so i guess is not permission problem, but not with the script.
The weired thing is if i run top, i can see the java processes running in each machine.
BTW: those 2 & is so it the script doesnt get stuck after running each jar
I recommend using SSH keys rather than expressing the password in a command (which gets logged and is visible to other users, not to mention its presence in your script). The github ssh key generation docs are pretty good for this (to add, append to the server's ~/.ssh/authorized_keys file).
Once you have generated a key on the client and added its pubkey to the server, you should be able to run:
for i in {1..11}
do
if [ $i -ne 6 ] # skip server six
then
cat myJar.jar |ssh user#l040101-ws$i.XXXX.XX.XX \
"cd someFolder; cat > myJar.jar; java -jar myJar.jar" &
fi
done
Note the single ampersand there, which is outside the quotes (so it is run on your client, not your server). You can't send an SSH session to the background because the parent would be killed.
I wrangled this into one line in order to minimize the number of connections (the first cat command dumps the file into standard output while the second cat command writes the standard input (the contents of myJar.jar) to the target location). I wasn't sure if I could just pipe it straight to java (cat myJar.jar |ssh user#host "cd someFolder; java -jar -"), so I left that alone.
I'm assuming you don't have to run the .jar from the parent of someFolder. It seems simpler to actually be in the target directory. If there's a chance the target directory does not exist, add mkdir -p someFolder; before the cd command. The -p will ensure nothing happens if the directory already exists. If you do have to run it from the parent, remove the cd command and replace "myJar.jar" with "someFolder/myJar.jar"

Executing java program from bash script does not work

I am trying to run a java program (weka) from a bash script. The script takes as arguments an inputfile, an outputfile and the content of file containing the command to run the java program (environment variable $CMD). The script does not work as I wish and informs me that I use an unknown option for java. I tried to echo the command that the program sends to the shell, and the output is exactly the right command. So I assume that the echo output and the command sent to the shell are not the same.
So please tell me: What did I do wrong?
What is the difference between the output I get...
echo "java $(cat $CMD) $in > $out"
...and the command the computer gets?
java "$(cat $CMD)" $in > $out
If more information is needed, please comment!
Edit:
For those familiar with weka (or familiar with java), this is what I want to get, and what is printed to me by echo:
java -cp /usr/share/java/weka.jar weka.filters.supervised.attribute.AttributeSelection -E "weka.attributeSelection.ClassifierSubsetEval -B weka.classifiers.bayes.NaiveBayes -T" -S "weka.attributeSelection.BestFirst -D 1 -N 14" -i /home/aldorado/weka_examples/iris.arff > /home/aldorado/weka_examples/irisselected131113.txt
Add set -x in before the line which causes trouble.
That will make the computer print the command again as it understood it. You will see something like
+ 'java' '-classpath weka.jar name.of.the.main.Class' 'inputFile' > 'outputFile'
Note that quotes which the shell uses to tell you "this was one word / argument for me". It's very useful to notice problems with white space and quoting.
Note that it is very hard to get something like java "$(cat $CMD)" $in > $out working. I suggest to move the java into $CMD. That will allow you to say:
bash "./$CMD" $in > $out
or, if you make the file executable:
"./$CMD" "$in" > $out
Use "$1" in the file $CMD to get a property quoted reference to "$in":
cp="weka.jar"
cp="$cp;other.jar"
cp="$cp;more.jar"
cp="$cp;foo.jar"
java -classpath "$cp" name.of.the.main.Class "$1"

Categories