为什么输出显示的顺序错误?

dro*_*del 1 java groovy

我正在尝试编写一个包装另一个命令的Groovy脚本,并且遇到了stdout/stderr命令的问题.我的脚本如下:

#!/usr/bin/env groovy
synchronized def output = ""
def process = "qrsh ${args.join(' ')}".execute()

def outTh = Thread.start {
  process.in.eachLine {
    output += it
    System.out.println "out: $it"
  }
}

def errTh = Thread.start {
  process.err.eachLine {
    output += it
    System.err.println "err: $it"
  } 
} 

outTh.join()
errTh.join()
process.waitFor()
System.exit(process.exitValue())
Run Code Online (Sandbox Code Playgroud)

我的问题是输出没有按正确的顺序出现在终端上.下面是包装器的输出.

[<cwd>] wrap.groovy -cwd -V -now n -b y -verbose ant target
waiting for interactive job to be scheduled ...
Your interactive job 2831303 has been successfully scheduled.
Establishing builtin session to host <host> ...
Buildfile: build.xml

BUILD FAILED
Target "target" does not exist in the project "null". 

Total time: 0 seconds
Your job 2831303 ("wrap.groovy") has been submitted
Run Code Online (Sandbox Code Playgroud)

下面是解包的命令输出.

[<cwd>] qrsh -cwd -V -now n -b y -verbose ant target
Your job 2831304 ("ant") has been submitted
waiting for interactive job to be scheduled ...
Your interactive job 2831303 has been successfully scheduled.
Establishing builtin session to host host ...
Buildfile: build.xml

BUILD FAILED
Target "target" does not exist in the project "null". 

Total time: 0 seconds
Run Code Online (Sandbox Code Playgroud)

为什么"你的工作已被提交"消息显示为一个演员阵容的第一行和另一个演员阵容的最后一行?我猜它与Java库有关,而不是Groovy.

Aar*_*lla 6

这是因为缓冲.读取stdout和stderr的线程在子进程写入时不会处理输出.相反,两个流都是缓冲的,因此除非孩子刷新流,否则您的进程将看不到任何内容.

当数据在路上时,哪个线程首先得到CPU?没有办法说出来.即使stdr的数据在stdout之前几毫秒到达,如果stdout线程现在有CPU,它将首先获取其数据.

您可以做的是使用Java NIO(通道)和单个线程,并首先处理来自stderr的所有输出,但仍然不能保证订单被保留.由于子进程和父进程之间的缓冲,您可以在看到另一个流的单个字节之前从一个流中获取4KB的文本.

遗憾的是,没有跨平台解决方案,因为Java没有API将两个流合并为一个.在Unix上,您可以运行命令sh -c cmd 2>&1.这会将stderr重定向到stdout.在父进程中,您可以只读取stdout并忽略stderr.

OS X也是如此(因为它基于Unix).在Windows上,您可以安装Perl或类似的工具来运行该过程; 这可以让你弄乱文件描述符.

PS:祈祷args从不包含空格.String.execute()是一个非常糟糕的运行过程的方法; 使用java.lang.ProcessBuilder来代替.