18

You can run a scala script as a linux shell script:

#!/bin/sh
exec scala "$0" "$@"
!#

println("Hello")

In one such script I need to load classes from a group of jars (which happen to be in the same directory as the script). If this were the REPL I could use :jar, but that's not available in script mode.

I'm trying to set the -classpath parameter:

#!/bin/sh
exec scala -classpath '.:./*.jar' "$0" "$@"
!#

import javax.media.jai.{JAI, RenderedOp}

but the compiler just can't find the classes:

error: object media is not a member of package javax
import javax.media.jai.{JAI, RenderedOp}
             ^

How do I include these jars?

1
  • 1
    Using ' will prevent glob expansion, so it will look for a file named *.jar. Commented Nov 25, 2011 at 2:04

7 Answers 7

10
exec scala -classpath ./*.jar $0 $@

will work

Sign up to request clarification or add additional context in comments.

2 Comments

No, it doesn't work for me. The import statement fails with an error message such as: error: object jai is not a member of package com.sun.media The class in question is definitely in one of the jars, I checked.
Hm. I don't know. It just works for me. I have checked with some jar
10

For some reason, the glob (*.jar) wasn't working. I was able to get the script running by putting in all the libraries by hand:

#!/bin/sh
exec scala -cp lib/jai_codec.jar:lib/jai_core.jar:lib/mlibwrapper_jai.jar $0 $@
!#

import javax.media.jai.{JAI, RenderedOp}

I don't know why the glob isn't working though.

Note that in this case I don't have the . in the classpath because the script itself is provided as an argument. In many cases though you will need to include it:

exec scala -cp .:lib/jai_codec.jar:lib/jai_core.jar:lib/mlibwrapper_jai.jar $0 $@

Based on this helpful post, I have a script header that pulls in every jar in a lib folder, even if the script (or the folder it's in) are symlinks.

#!/bin/sh
L=`readlink -f $0`
L=`dirname $L`/lib
cp=`echo $L/*.jar|sed 's/ /:/g'`
/usr/bin/env scala -classpath $cp $0 $@
exit
!#
  • The first line turns the given script location $0 into its actual location on disk, expanding symlinks.
  • The second line removes the script name and adds /lib
  • The third line creates a cp variable with all the jars separated by :
  • The fourth line runs scala, wherever it may be.
  • The fifth line exits. This probably isn't necessary, but it makes me feel better.

2 Comments

Shell globs work only when they match file names. This won't work in a path since ':' is a valid filename character. Also, when globs do match, they'll output a space-separated list of filenames.
Add the -savecompiled compiled option to make the script execute faster after the 1st time.
4

I recommend you use SBT instead to handle it. See the scripts wiki, which explains how to do something like this:

#!/usr/bin/env scalas
!#

/***
scalaVersion := "2.9.0-1"

libraryDependencies ++= Seq(
  "net.databinder" %% "dispatch-twitter" % "0.8.3",
  "net.databinder" %% "dispatch-http" % "0.8.3"
)
*/

import dispatch.{ json, Http, Request }
import dispatch.twitter.Search
import json.{ Js, JsObject }

def process(param: JsObject) = {
  val Search.text(txt)        = param
  val Search.from_user(usr)   = param
  val Search.created_at(time) = param

  "(" + time + ")" + usr + ": " + txt
}

Http.x((Search("#scala") lang "en") ~> (_ map process foreach println))

5 Comments

This works but it makes it makes running the simplest script take about 8 seconds, every time, any way around that to make it faster even in subsequent runs?
@MohamedRagab I think it can save up on some of the work done, but I'm not sure. A hot JVM is probably more important, such as you can get with Nailgun.
@DanielCSobral sure Nailgun would help, to my understanding the overhead of starting the JVM is below half a second, and SBT caching saves a bit more time of course. It would be great if some of the processing done here is avoided if not needed, I am playing with an attempt to check first in the bash script the existence of the JAR files in the ivy cache and then adding them to the classpath directly
@MohamedRagab Did you make any progress? Is there a way to start SCALA repl backed with .jars inside ivy2 cache?
A few years later, Ammonite offers a comprehensive solution for writing Scala scripts
2

To avoid to define classpath definition in every header you can define a function that wraps your scala executable in (for example) your .bashrc and make it visibile to outer scope:

...
function scala() {

    PARAMS=( "$@" )

    FILENAME=${PARAMS[0]}
    REMAINING_PARAMS=(${PARAMS[@]:1:${#PARAMS[@]}})

    CUSTOM_CLASSPATH_DEF=".:/whatever/*"
    /path/to/scala -cp "$CUSTOM_CLASSPATH_DEF" -Dscala.color=true "$FILENAME" "${REMAINING_PARAMS[@]}"
}

export -f scala 
...

Now you can define your header as:

#!/bin/bash
scala "$0" "$@"
exit 0
!#

(You can still use REPL typing 'scala' from command line)

Comments

1

http://www.scalaclass.com/node/10 This should work for you. I just fix the dependency according to it.

Comments

0

You could also put single quotes around the classpath with a glob, like this:

exec scala -classpath '.:*.jar' "$0" "$@"

Comments

0

The following works for me on Cygwin

#!/bin/sh

me=`basename "$0"`

exec scala "$me" "$@"

!#

  ...

2 Comments

This does not answer the question about jars on the classpath
This actually helped me to find those jars. Otherwise if I just do exec scala "$0" "$@", I will get an error message like "No such file or class on classpath".

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.