2011-02-10 40 views
0

我想写一个包装另一个命令的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()) 

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

[<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 

下面是unwrapped命令输出。

[<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 

为什么“您的工作已被提交”消息显示为一个演员的第一行,另一个演员的最后一行?我猜它与Java库有关,而不是Groovy。

回答

5

这是因为缓冲。读取stdout和stderr的线程在子进程写入时不会处理输出。相反,两个流都被缓冲,所以你的过程不会看到任何东西,除非子流刷新流)。

当数据在途中,哪个线程首先获取CPU?没有办法告诉。即使stderr的数据在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

+0

我理解与流之间的相对顺序的问题,但是应该在每个流内的顺序是否正确?我没有显示由源注释的行,但stdout顺序不正确。我也写了一个Perl包装器,它也有同样的问题。是否有可能将正在移动的线路直接写入终端?看起来,它仍然使得它stdout虽然.. – dromodel 2011-02-10 16:27:07

1

尝试将System.out.flush放到您的println后。如果我是正确的,消息以不同的顺序出现,因为System.out正在被缓冲。