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
execcommand is a shell built-in.$0expands 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