Почему выходные данные отображаются в неправильном порядке? - PullRequest
0 голосов
/ 10 февраля 2011

Я пытаюсь написать скрипт 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.

Ответы [ 2 ]

5 голосов
/ 10 февраля 2011

Это из-за буферизации. Потоки, которые читают stdout и stderr, не будут обрабатывать выходные данные в тот момент, когда они записаны дочерним процессом. Вместо этого оба потока буферизуются, поэтому ваш процесс не увидит ничего , если только дочерний процесс не сбросит потоки).

Когда данные находятся в пути, какой поток получает процессор первым? Там нет никакого способа, чтобы сказать. Даже если данные для stderr поступают за несколько миллисекунд до начала stdout, если поток stdout имеет сейчас ЦП, он сначала получит данные.

То, что вы можете сделать, это использовать Java NIO (каналы) и один поток и сначала обработать весь вывод из stderr, но это все равно не будет гарантировать сохранение порядка. Из-за буферизации между дочерним и родительским процессами вы можете получить 4 КБ текста из одного потока, прежде чем увидите один байт другого.

К сожалению, кроссплатформенного решения не существует, поскольку в Java нет API для объединения двух потоков в один. В Unix вы можете запустить команду с sh -c cmd 2>&1. Это перенаправило бы stderr на стандартный вывод. В родительском процессе вы можете просто прочитать stdout и проигнорировать stderr.

То же самое работает для OS X (поскольку она основана на Unix). В Windows вы можете установить Perl или аналогичный инструмент для запуска процесса; это позволяет вам связываться с файловыми дескрипторами.

PS: Молитесь, чтобы args никогда не содержал пробелов. String.execute() действительно плохой способ запустить процесс; используйте взамен java.lang.ProcessBuilder.

1 голос
/ 10 февраля 2011

Попробуйте поместить System.out.flush после того, как вы выполните печать. Если я прав, сообщения появляются в разных порядках, потому что System.out буферизируется.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...