How to run shell commands from the Scala REPL

This is an excerpt from the 1st Edition of the Scala Cookbook (partially modified for the internet). This is Recipe 14.4, “How to run a shell command from the Scala REPL.”

Problem

You want to be able to run a shell command from within the Scala REPL, such as listing the files in the current directory.

Solution

Run the command using the :sh REPL command, then print the output. The following example shows how to run the Unix ls -al command from within the REPL, and then show the results of the command:

scala> :sh ls -al
res0: scala.tools.nsc.interpreter.ProcessResult = `ls -al` (6 lines, exit 0)

scala> res0.show
total 24
drwxr-xr-x   5 Al  staff  170 Jul 14 17:14 .
drwxr-xr-x  29 Al  staff  986 Jul 14 15:27 ..
-rw-r--r--   1 Al  staff  108 Jul 14 15:34 finance.csv
-rw-r--r--   1 Al  staff  469 Jul 14 15:38 process.scala
-rw-r--r--   1 Al  staff  412 Jul 14 16:24 process2.scala

Alternatively you can import the scala.sys.process package, and then use the normal Process and ProcessBuilder commands described in Recipe 12.11, “Executing External Commands”:

scala> import sys.process._
import sys.process._

scala> "ls -al" !
total 24
drwxr-xr-x   5 Al  staff  170 Jul 14 17:14 .
drwxr-xr-x  29 Al  staff  986 Jul 14 15:27 ..
-rw-r--r--   1 Al  staff  108 Jul 14 15:34 finance.csv
-rw-r--r--   1 Al  staff  469 Jul 14 15:38 process.scala
-rw-r--r--   1 Al  staff  412 Jul 14 16:24 process2.scala
res0: Int = 0

Scala’s -i option

Although those examples show the correct approach, you can improve the situation by loading your own custom code when you start the Scala interpreter. For instance, I almost always start the REPL in my /Users/Al/tmp directory, and I have a file in that directory named repl-commands with these contents:

import sys.process._

def clear = "clear".!
def cmd(cmd: String) = cmd.!!
def ls(dir: String) { println(cmd(s"ls -al $dir")) }
def help {
    println("\n=== MY CONFIG ===")
    "cat /Users/Al/tmp/repl-commands".!
}

case class Person(name: String)
val nums = List(1, 2, 3)
val strings = List("sundance", "rocky", "indigo")
// lets me easily see the methods from StringOps
// with tab completion
val so = new collection.immutable.StringOps("")

With this setup, I start the Scala interpreter with the -i argument, telling it to load this file when it starts:

$ scala -i repl-commands

This makes those pieces of code available to me inside the REPL. For instance, I can clear my terminal window by invoking the clear method:

scala> clear

My ls method provides a directory listing:

scala> ls("/tmp")

With my cmd method I can run other external commands:

scala> cmd("cat /etc/passwd")

The help method uses the system cat command to display this file, which is helpful if I haven’t used it in a while. The nums and strings variables and Person class also make it easy to run quick experiments.

This approach is similar to using a startup file to initialize a Unix login session, like a .bash_profile file for Bash users, and I highly recommend it. As you use the REPL more and more, use this technique to customize its behavior.

To make this even easier, I created the following Unix alias and put it in my .bash_profile file:

alias repl="scala -i /Users/Al/tmp/repl-commands"

I now use this alias to start a REPL session, rather than starting it by typing scala:

$ repl

See Also

  • The “Executing external commands” recipes in Chapter 12 for more examples of executing external commands from Scala code