假设我们有以下 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
调用,对此行为没有影响。
最佳答案
This happens despite the fact that neither command (sleep nor echo) should be trying to read from stdin
请注意,该消息显示“tty 输出”,而不是“tty 输入”。所以它提示的是进程产生输出,而不是从标准输入读取。这很奇怪,有两个原因:
- 定期输出到 stdout 或 stderr 通常不会导致后台进程暂停
- 无论如何,您都会重定向 stdout 和 stderr,因此即使您的终端设置为在输出时暂停,在这种情况下也不应该发生这种情况。
输出暂停通常仅在进程使用 tty 功能而不仅仅是写入标准流时才会发生(例如写入屏幕上的特定位置或通常需要诅咒或类似功能的任何内容)。
你的程序没有这样做,但显然scala
二进制确实如此。至少我可以在尝试使用 scala className&
运行任何 Scala 程序时重现您的问题。 ,但是当我使用 java -cp /usr/share/scala/lib/scala-library.jar:. className&
时它就消失了相反。
所以我的结论是scala
以破坏后台进程的方式与 tty 交互,解决方法是使用 java
相反。
1 查看 /usr/bin/scala
后,看起来它正在调用 stty
保存和恢复终端设置。显然,如果进程在后台运行(因此未正确连接到 tty),则该方法不起作用,这就是进程被挂起的原因。
关于scala - 是什么导致这个 Scala 程序在后台运行时由于 tty 输出而挂起?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55504538/