Table of Contents
This is an excerpt from the Scala Cookbook (partially modified for the internet). This is Recipe 14.10, “How to use Scala as a scripting language.”
Problem
You want to use Scala as a scripting language on Unix systems, replacing other scripts you’ve written in a Unix shell (Bourne Shell, Bash), Perl, PHP, Ruby, etc.
Solution
Save your Scala code to a text file, making sure the first three lines of the script contain the lines shown, which will execute the script using the scala
interpreter:
#!/bin/sh exec scala "$0" "$@" !# println("Hello, world")
To test this, save the code to a file named hello.sh, make it executable, and then run it:
$ chmod +x hello.sh $ ./hello.sh Hello, world
As detailed in the next recipe, command-line parameters to the script can be accessed via an args
array, which is implicitly made available to you:
#!/bin/sh exec scala "$0" "$@" !# args.foreach(println)
Discussion
Regarding the first three lines of a shell script:
- The
#!
in the first line is the usual way to start a Unix shell script. It invokes a Unix Bourne shell. - The
exec
command is a shell built-in.$0
expands to the name of the shell script, and$@
expands to the positional parameters. - The
!#
characters as the third line of the script is how the header section is closed. A great thing about using Scala in your scripts is that you can use all of its advanced features, such as the ability to create and use classes in your scripts:
#!/bin/sh exec scala "$0" "$@" !# class Person(var firstName: String, var lastName: String) { override def toString = firstName + " " + lastName } println(new Person("Nacho", "Libre"))
Using the App
trait or main
method
To use an App
trait in a Scala script, start the script with the usual first three header lines, and then define an object
that extends the App
trait:
#!/bin/sh exec scala "$0" "$@" !# object Hello extends App { println("Hello, world") // if you want to access the command line args: //args.foreach(println) } Hello.main(args)
The last line in that example shows how to pass the script’s command-line arguments to the implicit main
method in the Hello
object. As usual in an App
trait object, the arguments are available via a variable named args
.
You can also define an object with a main
method to kick off your shell script action:
#!/bin/sh exec scala "$0" "$@" !# object Hello { def main(args: Array[String]) { println("Hello, world") // if you want to access the command line args: //args.foreach(println) } } Hello.main(args)
Building the classpath
If your shell script needs to rely on external dependencies (such as JAR files), add them to your script’s classpath using this syntax:
#!/bin/sh exec scala -classpath "lib/htmlcleaner-2.2.jar:lib/scalaemail_2.10.0-1.0.jar:lib/stockutils_2.10.0-1.0.jar" "$0" "$@" !#
You can then import these classes into your code as usual. The following code shows a complete script I wrote that retrieves stock quotes and mails them to me:
#!/bin/sh exec scala -classpath "lib/htmlcleaner-2.2.jar:lib/scalaemail_2.10.0-1.0.jar:lib/stockutils_2.10.0-1.0.jar" "$0" "$@" !# import java.io._ import scala.io.Source import com.devdaily.stocks.StockUtils import scala.collection.mutable.ArrayBuffer object GetStocks { case class Stock(symbol: String, name: String, price: BigDecimal) val DIR = System.getProperty("user.dir") val SLASH = System.getProperty("file.separator") val CANON_STOCKS_FILE = DIR + SLASH + "stocks.dat" val CANON_OUTPUT_FILE = DIR + SLASH + "quotes.out" def main(args: Array[String]) { // read the stocks file into a list of strings ("AAPL|Apple") val lines = Source.fromFile(CANON_STOCKS_FILE).getLines.toList // create a list of Stock from the symbol, name, and by // retrieving the price var stocks = new ArrayBuffer[Stock]() lines.foreach{ line => val fields = line.split("\\|") val symbol = fields(0) val html = StockUtils.getHtmlFromUrl(symbol) val price = StockUtils.extractPriceFromHtml(html, symbol) val stock = Stock(symbol, fields(1), BigDecimal(price)) stocks += stock } // build a string to output var sb = new StringBuilder stocks.foreach { stock => sb.append("%s is %s\n".format(stock.name, stock.price)) } val output = sb.toString // write the string to the file val pw = new PrintWriter(new File(CANON_OUTPUT_FILE)) pw.write(output) pw.close } } GetStocks.main(args)
I run this script twice a day through a crontab entry on a Linux server. The stocks.dat file it reads has entries like this:
AAPL|Apple KKD|Krispy Kreme NFLX|Netflix
this post is sponsored by my books: | |||
#1 New Release |
FP Best Seller |
Learn Scala 3 |
Learn FP Fast |
See Also
- More about the first three lines of these shell script examples at my site
- Recipe 14.13, “How to Make Your Scala Scripts Run Faster” for a way to make your scripts run faster