Skip to main content


Scala CLI accepts Scala scripts as files that end in .sc. Unlike .scala files, in scripts, any kind of statement is accepted at the top-level:
val message = "Hello from Scala script"

A script is run with the Scala CLI command:

Hello from Scala script

The way this works is that a script is wrapped in an object before it's passed to the Scala compiler, and a main method is added to it. In the previous example, when the script is passed to the compiler, the altered code looks like this:

object hello {
val message = "Hello from Scala script"

def main(args: Array[String]): Unit = ()

The name hello comes from the file name,

When a script is in a sub-directory, a package name is also inferred:

def hello = "Hello from Scala scripts"
import constants.messages

Please note: when referring to code from another script, the actual relative path from the project root is used for the package path. In the example above, as is located in the my-app/constants/ directory, to use the hello function you have to call constants.messages.hello.

When referring to code from a piped script, just use its wrapper name: stdin.

echo '@main def main() = println(stdin.message)' > PrintMessage.scala
echo 'def message: String = "Hello"' | scala-cli PrintMessage.scala

To specify a main class when running a script, use this command:

scala-cli my-app --main-class main_sc
Hello from Scala scripts

Both of the previous scripts ( and automatically get a main class, so this is required to disambiguate them. If a main class coming from a regular .scala file is present in your app's context, that will be run by default if the --main-class param is not explicitly specified.

When in doubt, you can always list the main classes present in your app by passing --list-main-classes.

echo '@main def main1() = println("main1")' > main1.scala
echo '@main def main2() = println("main2")' > main2.scala
echo 'println("on-disk script")' >
echo 'println("piped script")' | scala-cli --list-main-classes main1.scala main2.scala
stdin_sc script_sc main2 main1

Self executable Scala Script

You can define a file with the “shebang” header to be self-executable. Please remember to use scala-cli shebang command, which makes Scala CLI compatible with Unix shebang interpreter directive. For example, given this script:
#!/usr/bin/env -S scala-cli shebang
println("Hello world")

You can make it executable and run it, just like any other shell script:

chmod +x
Hello world

It is also possible to set Scala CLI command-line options in the shebang line, for example
#!/usr/bin/env -S scala-cli shebang --scala-version 2.13

The command shebang also allows script files to be executed even if they have no file extension, provided they start with the shebang header. Note that those files are always run as scripts even though they may contain e.g. valid .scala program.


You may also pass arguments to your script, and they are referenced with the special args variable:
#!/usr/bin/env -S scala-cli shebang

chmod +x
./ hello world

The name of script

You can access the name of the running script inside the script itself using the special scriptPath variable:
#!/usr/bin/env -S scala-cli shebang

chmod +x

Define source files in using directives

You can also add source files with the using directive //> using file in Scala scripts:
//> using file Utils.scala

object Utils {
val message = "Hello World"

Scala CLI takes into account and compiles Utils.scala.

Hello World

Difference with Ammonite scripts

Ammonite is a popular REPL for Scala that can also compile and run .sc files.

Scala CLI and Ammonite are similar, but differ significantly when your code is split in multiple scripts:

  • In Ammonite, a script needs to use import $file directives to use values defined in another script
  • With Scala CLI, all scripts passed can reference each other without such directives

On the other hand:

  • You can pass a single "entry point" script as input to Ammonite, and Ammonite finds the scripts it depends on via the import $file directives
  • Scala CLI requires all scripts to be passed beforehand, either one-by-one, or by putting them in a directory, and passing the directory to Scala CLI