假设我们有以下Scala(2.12)。它使用ProcessBuilder执行一组非常简单的命令(sleep
,后跟echo
)。该程序还捕获了stdout和stderr,并将所有带有相应前缀的行打印到Scala进程本身的stdout或stderr。
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"))
}
}
在OS X上zsh
5.6.2或GNU bash
4.4.23(1)中执行时,除非附加了stdin,否则由于tty输出,该过程将被挂起。尽管没有命令(sleep
和echo
)都不应该尝试从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
如果程序只是从前台开始运行,那么它也不会挂起。任何想法可能是什么原因造成的?
在启动stty -tostop
的终端中运行scala
或在调用这些相同命令然后由scala
调用的新脚本文件中运行cte
对此行为没有影响
答案 0 :(得分:5)
尽管事实上命令(睡眠或回声)都不应该尝试从标准输入中读取,但还是会发生这种情况
请注意,该消息显示为“ tty 输出”,而不是“ tty 输入”。因此,它抱怨该过程产生输出,而不是它从stdin读取。这很奇怪,原因有两个:
通常仅在进程使用tty功能而不是写入标准流(例如,写入屏幕上的特定位置或通常需要诅咒或类似功能的任何东西)时,才发生输出暂停。
您的程序不会执行此操作,但是显然scala
二进制文件¹。尝试使用scala className&
运行任何Scala程序时,至少我可以重现您的问题,但是当我改用java -cp /usr/share/scala/lib/scala-library.jar:. className&
时,它就消失了。
所以我的结论是scala
与tty的交互方式会破坏后台进程,而解决方法是改用java
。
¹查看/usr/bin/scala
之后,似乎正在调用stty
以保存和恢复终端设置。显然,如果该进程在后台运行(因此未正确连接到tty)是行不通的,这就是该进程被挂起的原因。