I am looking for a way to use the full path of the *.scala file that is executed by
scala /path/to/file/file.scala
The reason is that within my script I would like to use paths, which are relative to the location where file.scala is saved. So, say I want to call /path/to/file/file_second.scala from inside of file.scala while calling file.scala by
scala ./to./file/file.scala
as the directory I invoked scala from is /path (i.e. what System.getProperty("user.dir") would return). All I know priori is that file_second.scala is in the same directory as file.scala without actually knowing the full path.
I have tried. among others, getClass.getResource("").split(":")(1), but this will either return /path/./ or /tmp/scala.tmp/
I guess there must be a clean solution as scala -savecompiled will create a *.jar at exactly that directory I want to work on, but I just cannot figure out how... :/
Many thanks for any help.
INSTALLDIR="$( cd "$( dirname "BASHSOURCE[0]" )" && pwd )" exec scala -savecompiled -DPARAMINSTALLDIR=$INSTALLDIR "$0" "$@" !# val installdir = System.getProperty("PARAMINSTALLDIR") + java.io.File.separato - Wayne Jhukie 2012-06-04 20:08
The following script will print it's filesystem path. Three different approaches are tried in succession, until one of them succeeds.
import java.io.File
printf("[%s]\n",new File(getScriptName).getCanonicalPath)
System.exit(0)
def getScriptName:String = {
// this approach works on the sun jvm:
val jcmd = System.getProperty("sun.java.command","")
jcmd.split("""\s+""").toList.reverse.head match {
case name if new File(name).exists => name
case _ =>
// for this to work, you need to set -Dscala.script.name at script launch
System.getProperty("scala.script.name","") match {
case name if new File(name).exists => name
case _ =>
// last resort: derive script basename from stackdump
val basename = {
val filenames = new RuntimeException("").getStackTrace.map { it
=> it.getFileName
}
filenames.indexOf("NativeMethodAccessorImpl.java") match {
case -1 => filenames.reverse.head
case num => filenames(num - 1)
}
}
// then search the system PATH for the script file
import scala.collection.JavaConversions._
val sep = System.getProperty("path.separator")
val path = System.getenv("PATH").split(sep)
path.map(new File(_,basename)).find( _.exists ) match {
case Some(scriptfile) => scriptfile.getCanonicalPath
case _ => basename // better than nothing
}
}
}
}
The easiest and most portable is the 2nd approach, which depends on use of a wrapper script, as in the following example, for defining Property 'scala.script.name' before calling $SCALA_HOME/bin/scala. The wrapper script needs to be in your PATH ahead of $SCALA_HOME/bin, of course. The result is more bulletproof, but is only applicable to scripts.
#!/usr/bin/env sh
for SCRIPT_NAME; do true ; done # set SCRIPT_NAME to the last argument
JAVA_OPTS="$JAVA_OPTS -Dscala.script.name=${SCRIPT_NAME}"
$SCALA_HOME/bin/scala "$@"
Use of this wrapper around the scala launcher simplifies usage to the following:
#!/usr/bin/env scala
!#
printf("%s\n",System.getProperty("scala.script.name"))
Script preamble:
#!/bin/sh
export SCRIPT_PATH=$(readlink -f "$0") && exec scala "$0" "$@"
!#
Read the value with:
val scriptPath = System.getenv("SCRIPT_PATH")
export SCRIPT_DIR="$(dirname "$(readlink -f "$0")")" && exec scala "$0" "$@"
F. P. Freely 2018-08-03 15:48
I've done this in the past in Java. It's not pretty, and it may not be 100% reliable depending on the details of your class loader situation.
Here is some Scala code that prints out the location of its own class file.
import java.net.URL
object LocationTest extends App {
val classDirURL = LocationTest.getClass.getResource("LocationTest.class")
val classDirPath = classDirURL.getPath.replaceAll("%20", " ")
println(classDirPath)
}
prints
/Users/connor/desktop/LocationTest.class
You can invoke your program with a param:
scala -DPARAM_DIR=tmp Foo
and check for the param inside the program:
object Foo extends App {
val dir = System.getProperty ("PARAM_DIR")
println ("Dir: " + dir)
}
So this wouldn't (necessarily) use the same directory where Foo.scala is located, but it would be flexible, and can be told at runtime where to look for a class.
In normal cases you either bundle classes in a Jar, or you set a common classpath, and then you might have a package which is resolved relatively to the classpath:
For a class to.file.File in
/path/to/file/file.scala
it would be the classpath /path, for a class File in a package file
it would be /path/to/.
If your classpath is /path/to/file, the second class should be reachable simply by name.