Correctly quote shell commands in Scala
Once in a while you need to interact with the shell in Scala. This can be tricky,
because the Scala standard library is confusing. I already have a post about
running external processes in Scala. Sometimes you can’t prevent
calling the shell or even building or templating shell scripts. For these cases it is
paramount that you escape the input. In particular if you need to work with passwords,
containing special characters, such as $ (dollar) or ' (the single quote).
This is an old problem, but I could not find anything useful for Scala. I know Python has the shlex module, including quote functionality. Better a good copy than a bad original.
import scala.util.matching.Regex
object ShUtil {
// taken from python's shlex module
// https://docs.python.org/3/library/shlex.html
// https://github.com/python/cpython/blob/3.12/Lib/shlex.py#L323
private val unsafeCharRegex: Regex = "[^\\w@%+=:,./-]".r
def shQuote(s: String): String =
if s.isEmpty then "''"
else if unsafeCharRegex.findFirstIn(s).isEmpty then s
else
/*
use single quotes, and put single quotes into double quotes
the string ' is then quoted as "'"
*/
"'" + s.replace("'", "'\"'\"'") + "'"
}
Nothing fancy here, just the same in a different language.
On the first sight it might look strange in the example test below:
it should "escape nested strings" in {
val password = "s3'cr3t!"
val cmd = s"echo ${shQuote(password)}"
val shCmd = s"sh -c ${shQuote(cmd)}"
// yes, it looks awful, but it's correct
assert(shCmd == """sh -c 'echo '"'"'s3'"'"'"'"'"'"'"'"'cr3t!'"'"''""")
}
but it actually works very well.
Have fun.
