64

I just had a look at the new scala.sys and scala.sys.process packages to see if there is something helpful here. However, I am at a complete loss.

Has anybody got an example on how to actually start a process?

And, which is most interesting for me: Can you detach processes?

A detached process will continue to run when the parent process ends and is one of the weak spots of Ant.

UPDATE:

There seem to be some confusion what detach is. Have a real live example from my current project. Once with z-Shell and once with TakeCommand:

Z-Shell:

if ! ztcp localhost 5554; then
    echo "[ZSH] Start emulator"
    emulator                        \
    -avd    Nexus-One               \
    -no-boot-anim                   \
    1>~/Library/Logs/${PROJECT_NAME}-${0:t:r}.out   \
    2>~/Library/Logs/${PROJECT_NAME}-${0:t:r}.err   &
    disown
else
    ztcp -c "${REPLY}"
fi;

Take-Command:

IFF %@Connect[localhost 5554] lt 0 THEN
   ECHO [TCC] Start emulator
   DETACH emulator -avd Nexus-One -no-boot-anim
ENDIFF

In both cases it is fire and forget, the emulator is started and will continue to run even after the script has ended. Of course having to write the scripts twice is a waste. So I look into Scala now for unified process handling without cygwin or xml syntax.

Martin
  • 11,577
  • 16
  • 80
  • 110
  • 3
    `disown` is really a shell thing, not a Unix thing. All it does is avoid a shell behavior of sending SIGHUP to child processes on exit. – Daniel C. Sobral May 16 '11 at 19:13

7 Answers7

80

First import:

import scala.sys.process.Process

then create a ProcessBuilder

val pb = Process("""ipconfig.exe""")

Then you have two options:

  1. run and block until the process exits

    val exitCode = pb.!
    
  2. run the process in background (detached) and get a Process instance

    val p = pb.run
    

    Then you can get the exitcode from the process with (If the process is still running it blocks until it exits)

    val exitCode = p.exitValue
    

If you want to handle the input and output of the process you can use ProcessIO:

import scala.sys.process.ProcessIO
val pio = new ProcessIO(_ => (),
                        stdout => scala.io.Source.fromInputStream(stdout)
                          .getLines.foreach(println),
                        _ => ())
pb.run(pio)
michael.kebe
  • 10,916
  • 3
  • 44
  • 62
  • Thanks. Very nice example. Will help with some of the duplicate scripting I have. Mind you, what I was looking for (in unix terms) was "disown". Have a look at examples I just added. – Martin May 16 '11 at 11:34
  • 3
    for I/O handling there are also a few nice `#<` `#>` convenience methods. To bad the `#< (s: String)` is missing – Martin May 16 '11 at 17:18
  • I have accepted this answer as it contained ProcessIO which I used to get the here documents to work and that made this answer the most usefull. – Martin May 18 '11 at 07:28
  • 1
    is the only way to get both exitCode and string output to use ProcessIO? – Incerteza Mar 03 '14 at 06:50
  • but what if we need exitValue while process is still running in the background i.e process will keep running forever but we need exitValue also from the running processes – Aamir Sep 24 '15 at 07:45
  • @Aamir a running process does not have an exitValue (yet). – michael.kebe Sep 24 '15 at 08:35
  • not exitValue, i was actually referring to something that will give the status of running processes like some runningValue... – Aamir Sep 24 '15 at 08:38
26

I'm pretty sure detached processes work just fine, considering that you have to explicitly wait for it to exit, and you need to use threads to babysit the stdout and stderr. This is pretty basic, but it's what I've been using:

/** Run a command, collecting the stdout, stderr and exit status */
def run(in: String): (List[String], List[String], Int) = {
  val qb = Process(in)
  var out = List[String]()
  var err = List[String]()

  val exit = qb ! ProcessLogger((s) => out ::= s, (s) => err ::= s)

  (out.reverse, err.reverse, exit)
}
Alex Cruise
  • 7,939
  • 1
  • 27
  • 40
  • That looks very helpful. I now understand the syntax, which is more then I did before. Only I was looking for starting a process and let it run even after the script ended. – Martin May 16 '11 at 11:38
  • I was running a postgres script file using Process in scala, and my program was getting stuck because of huge output the script file outputs, after reading 100's of blogs this is the only answer that worked for me, thanks a million times @Alex Cruise – Aamir Aug 26 '16 at 05:10
8

Process was imported from SBT. Here's a thorough guide on how to use the process library as it appears in SBT.

https://github.com/harrah/xsbt/wiki/Process

Synesso
  • 37,610
  • 35
  • 136
  • 207
  • Interesting, the ! comes at the end. I would never guessed it from the scaladoc alone. – Martin May 16 '11 at 11:40
  • Looks like the doc is not up-to-date as one now needs to use ´import scala.sys.process._´ to use the implicit conversion. – Martin May 16 '11 at 17:09
  • Yes, see Scala 2.9 release notes (http://www.scala-lang.org/node/9483) - "scala.sys and scala.sys.process, which are imported from sbt.Process.". i.e., the package has changed. – Synesso May 16 '11 at 23:13
  • Updating the above link to [http://www.scala-sbt.org/release/docs/Process.html](http://www.scala-sbt.org/release/docs/Process.html) – chauhraj Jul 13 '17 at 16:18
7

Has anybody got an example on how to actually start a process?

import sys.process._ // Package object with implicits!
"ls"!

And, which is most interesting for me: Can you detach processes?

"/path/to/script.sh".run()

Most of what you'll do is related to sys.process.ProcessBuilder, the trait. Get to know that.

There are implicits that make usage less verbose, and they are available through the package object sys.process. Import its contents, like shown in the examples. Also, take a look at its scaladoc as well.

Daniel C. Sobral
  • 295,120
  • 86
  • 501
  • 681
7

The following function will allow easy use if here documents:

def #<<< (command: String) (hereDoc: String) =
{
    val process = Process (command)
    val io = new ProcessIO (
        in  => {in.write (hereDoc getBytes "UTF-8"); in.close},
        out => {scala.io.Source.fromInputStream(out).getLines.foreach(println)},
        err => {scala.io.Source.fromInputStream(err).getLines.foreach(println)})
    process run io
}

Sadly I was not able to (did not have the time to) to make it an infix operation. Suggested calling convention is therefore:

#<<< ("command") {"""
Here Document data
"""}

It would be call if anybody could give me a hint on how to make it a more shell like call:

"command" #<<< """
Here Document data
""" !
Martin
  • 11,577
  • 16
  • 80
  • 110
3

Documenting process a little better was second on my list for probably two months. You can infer my list from the fact that I never got to it. Unlike most things I don't do, this is something I said I'd do, so I greatly regret that it remains as undocumented as it was when it arrived. Sword, ready yourself! I fall upon thee!

psp
  • 12,138
  • 1
  • 41
  • 51
2

If I understand the dialog so far, one aspect of the original question is not yet answered:

  1. how to "detach" a spawned process so it continues to run independently of the parent scala script

The primary difficulty is that all of the classes involved in spawning a process must run on the JVM, and they are unavoidably terminated when the JVM exits. However, a workaround is to indirectly achieve the goal by leveraging the shell to do the "detach" on your behalf. The following scala script, which launches the gvim editor, appears to work as desired:

val cmd = List(
   "scala",
   "-e",
   """import scala.sys.process._ ; "gvim".run ; System.exit(0);"""
)
val proc = cmd.run

It assumes that scala is in the PATH, and it does (unavoidably) leave a JVM parent process running as well.

PhilW
  • 21
  • 1