【问题标题】:Why does output appear in wrong order?为什么输出以错误的顺序出现?
【发布时间】:2011-02-10 16:08:18
【问题描述】:

我正在尝试编写一个包装另一个命令的 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

下面是解包后的命令输出。

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

【问题讨论】:

    标签: java groovy


    【解决方案1】:

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

    当数据在路上时,哪个线程先获得CPU?没有办法说。即使stderr的数据在stdout之前几毫秒到达,如果stdout线程现在有CPU,它会先得到它的数据。

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

    不幸的是,没有跨平台的解决方案,因为 Java 没有将两个流合并为一个的 API。在 Unix 上,您可以使用 sh -c cmd 2&gt;&amp;1 运行命令。这会将标准错误重定向到标准输出。在父进程中,您可以只读取 stdout 并忽略 stderr。

    同样适用于 OS X(因为它是基于 Unix 的)。在 Windows 上,您可以安装 Perl 或类似的工具来运行该进程;这使您可以弄乱文件描述符。

    PS:祈祷args 永远不会包含空格。 String.execute() 是运行进程的一种非常糟糕的方式;请改用java.lang.ProcessBuilder

    【讨论】:

    • 我了解流之间的相对排序问题,但每个流中的顺序是否正确?我没有显示由源注释的行,但标准输出顺序不正确。我也写了一个 Perl 包装器,它也有同样的问题。移动的行是否有可能直接写入终端?看来它仍然可以进入标准输出..
    • 我尝试使用 shell 进行重定向,但这有其自身的问题。首先,很难正确地逃避争论。其次,我想保留stderr,因为这个程序只是一个包装器。最后,在 Solaris 上,在 shell 中重定向时出现“错误文件编号”错误
    • 流中的相对顺序不能改变。如果您在输出的不同位置看到一行出现,那么其他人会将其写入终端。我的猜测是包装好的脚本会启动另一个进程;不过,我不确定当您重定向包装器的 stdio 时它如何写入控制台。
    • 在从 Java 打印流时尝试添加类似“OUT”和“ERR”的内容。这应该有助于识别什么是什么。
    【解决方案2】:

    在执行 println 后尝试放置 System.out.flush。如果我是对的,那么消息会以不同的顺序出现,因为 System.out 正在被缓冲。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-12-12
      • 1970-01-01
      • 2021-05-26
      • 1970-01-01
      • 1970-01-01
      • 2014-02-21
      • 2015-03-04
      • 1970-01-01
      相关资源
      最近更新 更多