2011-02-10 3 views
0

다른 명령을 래핑하고 stdout/stderr 순서에 문제가있는 Groovy 스크립트를 작성하려고합니다. 내 스크립트는 다음과 같습니다 :출력이 잘못된 순서로 나타나는 이유는 무엇입니까?

#!/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 

한 캐스트의 첫 번째 행과 다른의 마지막 줄에 표시? Groovy가 아니라 Java 라이브러리와 관련이 있다고 생각합니다.

답변

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에 대한 동일한 작업 (그것은 유닉스 기반으로하기 때문에). Windows에서는 Perl 또는 이와 유사한 도구를 설치하여 프로세스를 실행할 수 있습니다. 파일 설명자를 엉망으로 만들 수 있습니다.

추 신 : args에는 공백이 포함되지 않도록기도하십시오. String.execute()은 프로세스를 실행하는 정말 나쁜 방법입니다. 대신 java.lang.ProcessBuilder을 사용하십시오.

+0

스트림 간의 상대적인 순서 문제는 이해하지만 각 스트림 내에서 순서가 맞아야합니까? 나는 소스에 의해 주석이 달린 선을 보여주지 않았지만 stdout 순서가 잘못되었습니다. 필자는 Perl 래퍼 (wrapper)도 썼고, 똑같은 문제가있다. 이동하는 회선이 터미널에 직접 쓰여질 수 있습니까? 그것도 여전히 표준 출력으로 만드는 것 같습니다. – dromodel

+0

쉘을 사용하여 리다이렉션을 시도했지만 그 자체의 문제가 있습니다. 첫째, 인수를 올바르게 벗어나기가 어렵습니다. 둘째,이 프로그램은 단순한 래퍼 (wrapper)이기 때문에 stderr를 유지하고 싶습니다. 마지막으로, Solaris에서 쉘에서 리디렉션 할 때 "잘못된 파일 번호"오류가 발생합니다. – dromodel

+0

스트림 내의 상대적인 순서는 변경할 수 없습니다. 출력의 다른 위치에 나타나는 행을 보면 다른 사람이 그 행을 터미널에 씁니다. 내 생각 엔 감싸 진 스크립트는 다른 프로세스를 시작한다. 당신이 래퍼의 stdio를 리디렉션 할 때 어떻게 콘솔에 쓸 수 있는지 잘 모르겠습니다. –

1

println을 실행 한 후에 System.out.flush를 입력 해보십시오. 내가 옳다면, System.out이 버퍼되고 있기 때문에 메시지가 다른 순서로 나타나고 있습니다.

관련 문제