Skip to main content

Shebang

This guide explains the differences between the run and shebang sub-commands, mainly covering how each of them parses its arguments.

shebang script headers

Before proceeding, let's discuss how scala-cli works in a script without the shebang command. Here is a simple hello.sc script with a shebang header:

hello.sc
#!/usr/bin/env -S scala-cli -S 3

println(args.size)
println(args.headOption)

And it works almost correctly.

chmod +x hello.sc
./hello.sc
0
None

And it also works.

./hello.sc -- Hello World
2
Some(Hello)

Note that the extra -- must be added to make it work. If it is not supplied, the result is:

./hello.sc Hello World
[error] Hello: not found
World: not found

If we modify our script slightly and use the shebang sub-command in the header, we will get the following:

hello.sc
#!/usr/bin/env -S scala-cli shebang -S 3

println(args.size)
println(args.headOption)
./hello.sc Hello World
2
Some(Hello)

shebang and the command line

Let's now see how the shebang command works straight from the command line.

Main.scala
object Main {
def main(args: Array[String]): Unit = println(args.mkString(" "))
}
scala-cli shebang Main.scala Hello world
Hello world
note

Please note that shebang changing how arguments are parsed means that every option after the first input will be treated as an argument to the app.

scala-cli shebang Main.scala -S 2.13 #-S 2.13 is not recognised as an option, but as app arguments
-S 2.13

If we try to do the same with the run sub-command, we get the following error:

scala-cli run Main.scala Hello world
[error]  Hello: not found
world: not found