Suppose we have the following Scala (2.12). It is using ProcessBuilder to execute a very trivial set of commands (a sleep
, followed by an echo
). The program also captures both stdout and stderr, and prints all lines - with those respective prefixes - to either stdout or stderr of the Scala process itself.
import scala.sys.process._
import scala.language.postfixOps
object BackgroundProcessRedirect {
def main(args: Array[String]) = {
val output = "sleep 5" #&& s"echo myoutput" lineStream_! ProcessLogger(line => System.err.println(s"stderr: $line"))
output.foreach(line => System.out.println(s"stdout: $line"))
}
}
When executed on OS X, either in zsh
5.6.2 or GNU bash
4.4.23(1), the process becomes suspended due to tty output unless stdin is attached. This happens despite the fact that neither command (sleep
nor echo
) should be trying to read from stdin.
# first, compile
scalac BackgroundProcessRedirect.scala
# now, run as a background process, redirecting stdout and stderr to files
scala BackgroundProcessRedirect >/tmp/scala.out 2>/tmp/scala.err &
# after 5 seconds, the process is suspended, as in the following jobs output
[1] + <PID> suspended (tty output) scala BackgroundProcessRedirect > /tmp/scala.out 2> /tmp/scala.err
# now, foreground the process; it will complete immediately
fg
# the output file contents are as expected
cat /tmp/scala.out
stdout: myoutput
# run the same thing again, but this time redirect stdin from /dev/null
scala BackgroundProcessRedirect </dev/null >/tmp/scala.out 2>/tmp/scala.err &
# now, the process completes normally after 5 seconds (not suspended), and the file contents are as expected
cat /tmp/scala.out
stdout: myoutput
If the program is simply run in the foreground to begin with, then it also doesn't become suspended. Any ideas what could be causing this?
Running stty -tostop
in either the terminal that launches scala
, or in a new script file that invokes these same commands, and is then invoked by scala
, has no effect on this behavior.
This happens despite the fact that neither command (sleep nor echo) should be trying to read from stdin
Note that the message says "tty output", not "tty input". So it's complaining about the fact that the process produces output, not that it reads from stdin. That's weird for two reasons:
Suspension on output usually only happens if the process uses tty features beyond just writing to the standard streams (such as writing to specific position on the screen or generally anything that requires curses or similar functionality).
Your program doesn't do this, but apparently the scala
binary does¹. At least I can reproduce your problem when trying to run any Scala program with scala className&
, but it goes away when I use java -cp /usr/share/scala/lib/scala-library.jar:. className&
instead.
So my conclusion is that scala
interacts with the tty in a way that breaks background processes and a workaround is to use java
instead.
¹ After looking at /usr/bin/scala
, it looks like it's calling stty
to save and restore the terminal settings. Apparently that doesn't work if the process is run in the background (and thus not properly connected to tty) and that's why the process is suspended.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With