How can I manage exception when using external processes in scala? - scala

When running a external command, exceptions are simply displayed to the std output. How can I manage them from inside the code ?
Example :
import sys.process._
("yes -y 100" #| "head -c 1").!
I simply get the following stacktrace.
java.io.IOException: Pipe closed
at java.io.PipedInputStream.checkStateForReceive(PipedInputStream.java:261)
at java.io.PipedInputStream.awaitSpace(PipedInputStream.java:269)
at java.io.PipedInputStream.receive(PipedInputStream.java:232)
at java.io.PipedOutputStream.write(PipedOutputStream.java:149)
at scala.sys.process.BasicIO$.loop$1(BasicIO.scala:236)
at scala.sys.process.BasicIO$.transferFullyImpl(BasicIO.scala:242)
at scala.sys.process.BasicIO$.transferFully(BasicIO.scala:223)
at scala.sys.process.ProcessImpl$PipeThread.runloop(ProcessImpl.scala:159)
at scala.sys.process.ProcessImpl$PipeSource.run(ProcessImpl.scala:179)
Any try catch seems to be ignored.

Printing to stdout is what .! does. You will need to use .! with custom process logger, i.e.:
import scala.sys.process._
("yes -y 100" #| "head -c 1").!(new ProcessLogger {
override def buffer[T](f: => T): T = f
override def out(s: => String): Unit = ()
override def err(s: => String): Unit = ()
})
.... or just .!! to get a string

Related

Getting command and arguments as String in Scala

A small program that can be packed as a jar and you run it in the console like this:
scala program.jar -v --debug --file "/home/user/..."
Is there a way to get the whole command with all the parameters as a String?
object Main {
def main(args: Array[String]): Unit = {
println("???")
// should print: "scala program.jar -v --debug --file "/home/user/..."
}
}
You can't reliably get the complete command line (see this discussion) but you can get close.
object Main {
def main(args: Array[String]): Unit = {
val allargs = "scala" +: System.getProperty("sun.java.command").split(" ").tail
println(allargs.mkString(" "))
}
}
Use mkstring method of array. As argument you can place separator between array entities.
Ex: http://ideone.com/
val argss = Array("a", "b", "c --d")
println(argss.mkString(" "))

Run Scala application on different process

Hi I want to create a program that starts another application on a new process:
object A{
def main(args: Array[String]) {
Process(????).run
println("new process has been created")
}
}
the application that should run on the new process:
object B{
def main(args: Array[String]) {
print("Hello world")
}
}
I know I can run a script using Process(script_string_to_run), but I don't know how to make it run another application..
Run Scala program from another Scala program
Scala Program
B.scala
object B {
def main(args: Array[String]): Unit = println("hello world")
}
Now execute the above program like this from another Scala program.
scala> import sys.process._
import sys.process._
scala> "scala B.scala" !!
warning: there was one feature warning; re-run with -feature for details
res1: String =
"hello world
"
Note that the file name of the file is same as the object name containing main method. This way scala would recognise the main class/object and execute the main method of the program. If the name of the file is not the name of the object containing the main method then It would not know what to execute.
! gives the exit status of the program after execution whereas !! gives the output of the program.
scala> import sys.process._
import sys.process._
scala> val status = "scala B.scala" !
warning: there was one feature warning; re-run with -feature for details
hello world
status: Int = 0
scala> val output = "scala B.scala" !!
warning: there was one feature warning; re-run with -feature for details
output: String =
"hello world
Scala Script
Test.sh
#!/usr/bin/env scala
object B{
def main(args: Array[String]) {
print("Hello world")
}
}
B.main(Array[String]())
Now Just run the B program using
"./Test.sh" !!
Command Line Activity
➜ cd demo
➜ demo ./Test.sh
zsh: permission denied: ./Test.sh
➜ demo chmod +rwx Test.sh
➜ demo ./Test.sh
Hello world%
➜ demo ./Test.sh
Hello world%
➜ demo scala
Welcome to Scala 2.11.8 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_45).
Type in expressions for evaluation. Or try :help.
scala> import sys.process._
import sys.process._
scala> "./Test.sh" !!
warning: there was one feature warning; re-run with -feature for details
res0: String =
"Hello world
"
scala> :q

How do you write to and read from an external process using scalaz streams

I would like to be able to send data from a scalaz stream into an external program and then get the result of that item back in about 100ms in the future. Although I was able to do this with the code below by zipping the output stream Sink with the input stream Process and then throwing away the Sink side effect, I feel like this solution may be very brittle.
If the external program has an error for one of the input items everything will be out of sync. I feel like the best bet would be to send some sort of incremental ID into the external program which it can echo back out in the future so that if an error occurs we can resync.
The main trouble I am having is joining together the result of sending data into the external program Process[Task, Unit] with the output of the program Process[Task, String]. I feel like I should be using something from wyn but not really sure.
import java.io.PrintStream
import scalaz._
import scalaz.concurrent.Task
import scalaz.stream.Process._
import scalaz.stream._
object Main extends App {
/*
# echo.sh just prints to stdout what it gets on stdin
while read line; do
sleep 0.1
echo $line
done
*/
val p: java.lang.Process = Runtime.getRuntime.exec("/path/to/echo.sh")
val source: Process[Task, String] = Process.repeatEval(Task{
Thread.sleep(1000)
System.currentTimeMillis().toString
})
val linesR: stream.Process[Task, String] = stream.io.linesR(p.getInputStream)
val printLines: Sink[Task, String] = stream.io.printLines(new PrintStream(p.getOutputStream))
val in: Process[Task, Unit] = source to printLines
val zip: Process[Task, (Unit, String)] = in.zip(linesR)
val out: Process[Task, String] = zip.map(_._2) observe stream.io.stdOutLines
out.run.run
}
After delving a little deeper into the more advanced types. It looks like Exchange does exactly what I want.
import java.io.PrintStream
import scalaz._
import scalaz.concurrent.Task
import scalaz.stream._
import scalaz.stream.io._
object Main extends App {
/*
# echo.sh just prints to stdout what it gets on stdin
while read line; do
sleep 0.1
echo $line
done
*/
val program: java.lang.Process = Runtime.getRuntime.exec("./echo.sh")
val source: Process[Task, String] = Process.repeatEval(Task{
Thread.sleep(100)
System.currentTimeMillis().toString
})
val read: stream.Process[Task, String] = linesR(program.getInputStream)
val write: Sink[Task, String] = printLines(new PrintStream(program.getOutputStream))
val exchange: Exchange[String, String] = Exchange(read, write)
println(exchange.run(source).take(10).runLog.run)
}

Using scala.sys.process with timeout

I find it extreemly cool to use standard syntax like
import scala.sys.process._
val countLogger = ProcessLogger(line => {println ("out line: " + line)},
line => {println ("err line: " + line)})
val exitCode = ("cat prog.c" #&& "gcc prog.c -o prog -lm"
#&& "echo running, this may hang" #&& "prog.exe") ! countLogger
println("exitCode = " + exitCode)
It however happens that last process hangs. Is it possible to kill it on timeout?
You can wrap your process in a Future(blocking(_)) and if it doesn't return after the time-out, you call process.destroy().
That's what I have done for my small Processor library, e.g. see here. Instead of using ! to eagerly wait for the exit-code, you use the run method. Here is an adaption from the README:
import scala.concurrent._
import ExecutionContext.Implicits.global
import scala.sys.process._
val p = "sleep 100".run() // start asynchronously
val f = Future(blocking(p.exitValue())) // wrap in Future
val res = try {
Await.result(f, duration.Duration(2, "sec"))
} catch {
case _: TimeoutException =>
println("TIMEOUT!")
p.destroy()
p.exitValue()
}

Redirect stdout in another thread

I am trying to write a test that will redirect the stdout of a main method, but it seems that once I call the main, it seems to start on another thread and I cannot capture the output. Here is the code:
This works:
val baos = new ByteArrayOutputStream
val ps = new PrintStream(baos)
System.setOut(ps)
print("123")
Assert.assertEquals("123", baos.toString)
This does not:
val baos = new ByteArrayOutputStream
val ps = new PrintStream(baos)
System.setOut(ps)
GameRunner.main(_)
Assert.assertEquals("123", baos.toString)
....
object GameRunner {
def main(args: Array[String]) {
print("123")
How can I catch the call to print in my test?
*I have also tried scala.Console.setOut
EDIT
I do notice that running GameRunner.main(_) does not even list anything in the console when I am not redirecting. What is causing this?
print is really Predef.print which calls Console.print. Even though you call System.setOut I don't know if that has an impact on Console.print. Try to call Console.setOut or try:
Console.withOut(ps)(GameRunner.main(null))
The other possibility is that by calling GameRunner.main(_) you are not executing anything (as may be it's just returning the function (args: Array[String]) => GameRunner.main(args)?. Should be quick to rule that out.
Edit yep:
scala> object A { def main(args: Array[String]) { println("1") } }
defined module A
scala> A.main(null)
1
scala> A.main(_)
res1: Array[String] => Unit = <function1>