如何在Scala中获取当前脚本或类名?

3

我希望我的Scala程序能够编程方式确定其脚本文件名或类名,将字符串存储在变量program中并打印出名称。

Java有几种方法可以实现此功能。


4
你尝试过采用你提到的Java解决方案了吗?看起来它们都应该可以工作,是吗? - Tomasz Nurkiewicz
5个回答

5
我认为这是最简单的:
val program = new Exception().getStackTrace.head.getFileName

2

根据0__的提示解析堆栈跟踪应该可以处理大部分情况,特别是当包含主方法的对象不在同名文件中时:

package utils

trait ProgramInfo {
  val programInfo = try { 
    throw new RuntimeException("x")
  } catch { 
    case e: RuntimeException =>
      val arr = new java.io.CharArrayWriter()
      val buffer = new java.io.PrintWriter(arr)
      e.printStackTrace(buffer)
      val trace = arr.toString 
      val lines = io.Source.fromString(trace)
      val pat = """^.*at.*\.main\(([^:]*)(:.*)?\).*$""".r
      lines.getLines().collectFirst{case pat(n, l) => n}.getOrElse("<none>")
  }
}

object ProgramInfo extends ProgramInfo

然后你这样调用它:
println(utils.ProgramInfo.programInfo)

或者你将其混合到主对象中。
object A extends utils.ProgramInfo {
   def main(args: Array[String]) {
     println(programInfo)
   }
}

无论代码是否包含在对象中,scala A.script 都可以用于脚本。同时,使用 scalac 编译并运行 scala A 也是可行的。但是,在 REPL 中运行时将返回 <none>


那几乎可以用。当我运行它时,它会打印空括号“()”。 - mcandre
@mcandre,奇怪。我猜在val trace定义之后添加println(trace),看看是否出现.main(xxx) - huynhjl
啊,问题在于我的书写错误。我试图编写一个函数 def 来返回程序名称,但是Scala将 def 视为无返回值的函数。你知道得越多,你就会发现这个问题。 - mcandre
此外,Scala 在 package 语句上出了问题。 - mcandre
@mcandre,哦,是的,这个包只能用于编译后的代码,而不能用于REPL。 - huynhjl
有什么想法吗? 她的工作方式与解释器不同,但编译器却很烦人。 - mcandre

1

不太确定你在寻找什么...

$ scala -e 'println( "I am " + getClass.getName )'

给我

"I am Main$$anon$1"

而且

$ scala -e 'try { sys.error( "" )} catch { case e => println( "I am " + e.getStackTrace()( 3 ))}'

给我

"I am Main.main(scalacmd2873893687624153305.scala)"

例如,如果我们有一个名为Hello.scala的程序,我想在Hello#main中以编程方式检测类名为“Hello”,将其保存到变量program中,并打印出来。 - mcandre
1
在Scala中,你不会从类内部运行main方法 - 你会在一个对象中运行它。如果你是从一个类实例调用的话,上面的getClass.getName将会起作用。(它也可以在一个对象中工作,但可能不会看起来像你期望的那样。) - Luigi Plinge

1
// With help from huynhjl
// https://dev59.com/JV3Ua4cB1Zd3GeqP-jsm

import scala.util.matching.Regex.MatchIterator

object ScriptName {
    val program = {
        val filenames = new RuntimeException("").getStackTrace.map { t => t.getFileName }
        val scala = filenames.indexOf("NativeMethodAccessorImpl.java")

        if (scala == -1)
            "<console>"
        else
            filenames(scala - 1)
    }

    def main(args: Array[String]) {
        val prog = program
        println("Program: " + prog)
    }
}

罗塞塔代码


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

val program = args(0)
println(program)

谢谢你的代码片段。你能否将它写成可以与 scalac 编译器兼容的形式? - mcandre

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接