This is an excerpt from the 1st Edition of the Scala Cookbook (partially modified for the internet). This is Recipe 12.17, “How to handle wildcard characters when running external commands in Scala.”
Problem
You want to use a Unix shell wildcard character, such as *
, when you execute an external command in a Scala application.
Solution
In general, the best thing you can do when using a wildcard character like *
is to run your command while invoking a Unix shell. For instance, if you have .scala files in the current directory and try to list them with the following command, the command will fail:
scala> import scala.sys.process._ import scala.sys.process._ scala> "ls *.scala".! ls: *.scala: No such file or directory res0: Int = 1
But by running the same command inside a Bourne shell, the command now correctly shows the .scala files (and returns the exit status of the command):
scala> val status = Seq("/bin/sh", "-c", "ls *.scala").! AndOrTest.scala Console.scala status: Int = 0
Discussion
Putting a shell wildcard character like *
into a command doesn’t work because the *
needs to be interpreted and expanded by a shell, like the Bourne or Bash shells. In this example, even though there are files in the current directory named AndOrTest.scala and Console.scala, the first attempt doesn’t work. These other attempts will also fail as a result of the same problem:
scala> "echo *".! * res0: Int = 0 scala> Seq("grep", "-i", "foo", "*.scala").! grep: *.scala: No such file or directory res1: Int = 2 scala> Seq("ls", "*.scala").! ls: *.scala: No such file or directory res2: Int = 1
In each example, you can make these commands work by invoking a shell in the first two parameters to a Seq
:
val status = Seq("/bin/sh", "-c", "echo *").! val status = Seq("/bin/sh", "-c", "ls *.scala").! val status = Seq("/bin/sh", "-c", "grep -i foo *.scala").!
An important part of this recipe is using the -c
argument of the /bin/sh command. The sh
manpage describes this parameter as follows:
-c string If the -c option is present, then commands are read from string.
If there are arguments after the string, they are assigned to the positional parameters, starting with $0
.
As an exception to this general rule, the -name
option of the find
command may work because it treats the *
character as a wildcard character itself. As a result, the following find
command finds the two files in the current directory without having to be run in a shell:
scala> val status = Seq("find", ".", "-name", "*.scala", "-type", "f").! ./AndOrTest.scala ./Console.scala status: Int = 0
However, as shown, other commands generally require that the *
wildcard character be interpreted and expanded by a shell.
this post is sponsored by my books: | |||
#1 New Release |
FP Best Seller |
Learn Scala 3 |
Learn FP Fast |