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

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

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

Both of the previous scripts ( and automatically get a main class, so this is required to disambiguate them.

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


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
# 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