Run scala script from shell script

2019-09-11 23:53发布

问题:

I have a Scala project that contains both Scala classes/objects as well as scripts. I am trying to run the scripts (that use the classes/objects to perform various actions) from command line. The way I am looking to do this is to create a shell that would be passed as argument the name of the script, as well as the arguments that the script itself takes.

My folder structure is as follows:

runner.sh -> script I am trying to implement to run the Scala scripts
bin/ -> script I am trying to implement to run the Scala scripts
lib/ -> -> rest of scala project, packed as multiple jars 

I am currently creating a variable LIB_CLASSPATH in the shell script that contains the contents of the lib/ folder and I am trying to run my scrips as follows:

java -cp $LIB_CLASSPATH ./bin/<name_of_script.scala> <parameters of the script>

I always get back the same error:

Could not find or load main class: ..lib/<name of jar> 
-- <name of jar> is actually the first jar in the lib/ folder

I could use some guidance :)

Thanks

回答1:

You can use Ammonite (written by @lihaoyi).

It's a combination of a better Scala REPL, Shell, OPs library, and Script runner. See the docs here - http://www.lihaoyi.com/Ammonite/

10 min talk showing how is it better for scripting & shell - https://www.youtube.com/watch?v=wk2-ZsQU358

I think it can be a good starting point as well. Talk was given by me :)



回答2:

I would recommend using sbt native packager. See:

https://github.com/sbt/sbt-native-packager

You add this line to build.sbt:

enablePlugins(JavaAppPackaging)

And define a main class:

mainClass in Compile := Some("org.shapelogic.sc.javafx.ViewGui")

To run:

sbt stage

It create startup script located in a place like this:

target/universal/stage/bin/shapelogic

Here is an example of how I used the script:

target/universal/stage/bin/shapelogic -- -i image/440px-Lenna.png 

If you want to run another class you can do this:

target/universal/stage/bin/shapelogic -main "org.shapelogic.sc.script.Threshold" -- i image/440px-Lenna.png 


回答3:

Here's a script that will do what you want. It's based on a script I use, but stripped down for clarity:

#!/bin/bash

# copies of or symlinks to all required jar files
# must be in one of the directories referenced by CP

if [ $# -eq 0 ]; then
  echo "usage: ${0##*} <scala-script-path>" 1>&2
  exit 1
fi

REQUIRED_JAR_DIRECTORIES=(
"/usr/local/olib/*"
"/usr/local/jlib/*"
)
CP=$(echo "${REQUIRED_JAR_DIRECTORIES[@]}" | tr ' ' ':')
case `uname` in
CYGWIN*)
  CP=`cygpath -p -w "$CP"`
  ;;
esac
scala -deprecation -feature -classpath "$CP" "$@"

The easiest way to use this script is to place it in your PATH, and then add the following hash-bang line to the top of your scala script. Assuming you call this script 'scalaRunner', here's a standalone working scala script:

#!/usr/bin/env scalaRunner

println("Hello World!")

Here's one that's slightly more elaborate:

#!/usr/bin/env scalaRunner

if( args.isEmpty ){
  printf("usage: <arg1> [<arg2> ..]\n")
  sys.exit(1)
}
for( (arg,i) <- args.zipWithIndex ){
  printf("arg %d: %s\n",i,arg)
}

If you import anything, just make sure all required jars are in the REQUIRED_JAR_DIRECTORIES.

In practice, you may want something more flexible than this script, especially with regard to specifying where required jars reside.