2009-05-19 5 views
50

클라이언트 컴퓨터에서 네트워킹 문제가 발생하면 몇 가지 명령 줄을 실행하고 그 결과를 직접 전자 메일로 보내고 싶습니다.Runtime.exec를 호출 할 때 stdout 캡처

Runtime.exec을 사용하면 임의의 명령을 실행할 수 있지만 결과를 String으로 수집하는 것이 더 흥미 롭습니다.

출력을 파일로 리디렉션 한 다음 파일에서 읽을 수 있다는 것을 알았지 만, 필자의 말로는 더 우아한 방법이 있다고 말하고 있습니다.

제안 사항?

+0

[기사] (http://www.javaworld.com/javaworld/jw-12-2000/jw-1229-traps.html)을보십시오. – kgiannakakis

답변

46

프로세스에서 std out과 std err을 캡처해야합니다. 그런 다음 std를 파일/메일 또는 유사한 것으로 쓸 수 있습니다.

자세한 내용은 this article을 참조하십시오. 특히 StreamGobbler 메커니즘을 사용하여 별도의 스레드에서 stdout/err을 캡처합니다. 이것은 차단을 방지하는 데 필수적이며 제대로하지 않으면 수많은 오류의 원인이됩니다!

+0

커맨드가 많은 출력을 주면 Gobblers가 출력을 완료하기 전에 코드 흐름이 계속 진행됩니다. 돌아 오기 전에 .join()을 호출해야합니다. – Zitrax

+0

_ 매우 효과적이고 간단한 방법입니다. 한 가지주의 할 점은 Main 메소드의'cmd' 배열 초기화가 Windows 7 용으로 약간 날짜가있는 것 같습니다. cmd 배열을 NT 스타일로 초기화하는 마지막 "else"절을 추가하여 다른 ' 그 외의 경우 (osName.equals ("Windows NT")'false가 다시 나타남 – depthfirstdesigner

+0

SteamGobbler의 솔루션은 Windows 서버 전용입니까? Unix를 사용하는 경우에는 발생하지 않습니까? – Dejell

13

ProcessBuilder을 사용하십시오. start()를 호출하면 stderr 및 stdout 스트림을 얻을 수있는 Process 객체가 생깁니다.

업데이트 : ProcessBuilder는 더 많은 제어 기능을 제공합니다. 당신은 그것을 사용할 필요는 없지만 장기적으로는 더 쉽게 찾을 수 있습니다. 특히 stderr를 stdout으로 리디렉션하는 기능은 하나의 스트림 만 빨아 들여야 함을 의미합니다.

+3

그리고'OutputStream'에서 어떻게 출력 할 수 있습니까? – pihentagy

2

Runtime.exec()는 실행 한 명령의 출력을 추출 할 수있는 Process 개체를 반환합니다.

1

Runtime.exec을 사용하면 프로세스가 제공됩니다. 이것들을 사용하면 getInputStream을 사용하여이 프로세스의 표준 출력을 얻고,이 입력 스트림을 StringBuffer를 통해 String으로 넣을 수 있습니다.

5

Plexus Utils을 사용하면 Maven에서 모든 외부 프로세스를 실행하는 데 사용됩니다. jcabi-log에서

Commandline commandLine = new Commandline(); 
commandLine.setExecutable(executable.getAbsolutePath()); 

Collection<String> args = getArguments(); 

for (String arg : args) { 
    Arg _arg = commandLine.createArg(); 
    _arg.setValue(arg); 
} 

WriterStreamConsumer systemOut = new WriterStreamConsumer(console); 
WriterStreamConsumer systemErr = new WriterStreamConsumer(console); 

returnCode = CommandLineUtils.executeCommandLine(commandLine, systemOut, systemErr, 10); 
if (returnCode != 0) { 
    // bad 
} else { 
    // good 
} 
+1

외부 라이브러리를 사용해야하는 이유 핵심 언어가 완벽하게 적합한 대안을 가지고있는 경우 – PaulJWilliams

+1

시작 부분에 약간의 차이가있을 수 있습니다. 1. Process.waitFor()를 호출하면 이것은 프로세스 출력을 읽어야한다는 것을 의미합니다. 그렇지 않으면 프로세스는 출력 버퍼 (콘솔 출력)가 사용 가능할 때까지 대기합니다. 이 경로를 선택하면 (직접 출력을 얻음) waitFor()를 사용하면 안됩니다. 2. 폴링하면 출력을 읽기 위해 기다리는 동안 코드를 직접 처리해야합니다. Plexus Utils - 246k-와 같은 라이브러리의 목적은 바퀴를 처음부터 끝까지 다시 재발하는 것을 피하는 것입니다. :) –

+0

개미는 똑같은 일을합니다. 원한다면 사용할 수 있습니다. 호출 할 수있는 핵심 작업이 있습니다. (적절한 초기화 된 개미 문맥으로)이 작업을 수행 할 수 있지만, Plexus Utils가 더 작기 때문에 (당신은 cli 패키지를 제외한 모든 것을 제거 할 수 있습니다. 즉, 50k 미만을 의미합니다), Maven 2에 포함 된 이후) –

2

VerboseProcess 유틸리티 클래스는 당신을 도울 수 :

String output = new VerboseProcess(
    new ProcessBuilder("executable with output") 
).stdout(); 

유일한 종속성이 필요합니다이 내 헬퍼 클래스 년 동안 사용하고있다

<dependency> 
    <groupId>com.jcabi</groupId> 
    <artifactId>jcabi-log</artifactId> 
    <version>0.7.5</version> 
</dependency> 
2

. 작은 클래스 하나. JVM 리소스 누출을 수정하는 JavaWorld streamgobbler 클래스가 있습니다. JVM6 및 JVM7에 대해서는 여전히 유효한 지 모르지만 다치게하지는 않습니다. 도우미는 나중에 사용할 수 있도록 출력 버퍼를 읽을 수 있습니다.

import java.io.*; 

/** 
* Execute external process and optionally read output buffer. 
*/ 
public class ShellExec { 
    private int exitCode; 
    private boolean readOutput, readError; 
    private StreamGobbler errorGobbler, outputGobbler; 

    public ShellExec() { 
     this(false, false); 
    } 

    public ShellExec(boolean readOutput, boolean readError) { 
     this.readOutput = readOutput; 
     this.readError = readError; 
    } 

    /** 
    * Execute a command. 
    * @param command command ("c:/some/folder/script.bat" or "some/folder/script.sh") 
    * @param workdir working directory or NULL to use command folder 
    * @param wait wait for process to end 
    * @param args 0..n command line arguments 
    * @return process exit code 
    */ 
    public int execute(String command, String workdir, boolean wait, String...args) throws IOException { 
     String[] cmdArr; 
     if (args != null && args.length > 0) { 
      cmdArr = new String[1+args.length]; 
      cmdArr[0] = command; 
      System.arraycopy(args, 0, cmdArr, 1, args.length); 
     } else { 
      cmdArr = new String[] { command }; 
     } 

     ProcessBuilder pb = new ProcessBuilder(cmdArr); 
     File workingDir = (workdir==null ? new File(command).getParentFile() : new File(workdir)); 
     pb.directory(workingDir); 

     Process process = pb.start(); 

     // Consume streams, older jvm's had a memory leak if streams were not read, 
     // some other jvm+OS combinations may block unless streams are consumed. 
     errorGobbler = new StreamGobbler(process.getErrorStream(), readError); 
     outputGobbler = new StreamGobbler(process.getInputStream(), readOutput); 
     errorGobbler.start(); 
     outputGobbler.start(); 

     exitCode = 0; 
     if (wait) { 
      try { 
       process.waitFor(); 
       exitCode = process.exitValue();     
      } catch (InterruptedException ex) { } 
     } 
     return exitCode; 
    } 

    public int getExitCode() { 
     return exitCode; 
    } 

    public boolean isOutputCompleted() { 
     return (outputGobbler != null ? outputGobbler.isCompleted() : false); 
    } 

    public boolean isErrorCompleted() { 
     return (errorGobbler != null ? errorGobbler.isCompleted() : false); 
    } 

    public String getOutput() { 
     return (outputGobbler != null ? outputGobbler.getOutput() : null);   
    } 

    public String getError() { 
     return (errorGobbler != null ? errorGobbler.getOutput() : null);   
    } 

//******************************************** 
//********************************************  

    /** 
    * StreamGobbler reads inputstream to "gobble" it. 
    * This is used by Executor class when running 
    * a commandline applications. Gobblers must read/purge 
    * INSTR and ERRSTR process streams. 
    * http://www.javaworld.com/javaworld/jw-12-2000/jw-1229-traps.html?page=4 
    */ 
    private class StreamGobbler extends Thread { 
     private InputStream is; 
     private StringBuilder output; 
     private volatile boolean completed; // mark volatile to guarantee a thread safety 

     public StreamGobbler(InputStream is, boolean readStream) { 
      this.is = is; 
      this.output = (readStream ? new StringBuilder(256) : null); 
     } 

     public void run() { 
      completed = false; 
      try { 
       String NL = System.getProperty("line.separator", "\r\n"); 

       InputStreamReader isr = new InputStreamReader(is); 
       BufferedReader br = new BufferedReader(isr); 
       String line; 
       while ((line = br.readLine()) != null) { 
        if (output != null) 
         output.append(line + NL); 
       } 
      } catch (IOException ex) { 
       // ex.printStackTrace(); 
      } 
      completed = true; 
     } 

     /** 
     * Get inputstream buffer or null if stream 
     * was not consumed. 
     * @return 
     */ 
     public String getOutput() { 
      return (output != null ? output.toString() : null); 
     } 

     /** 
     * Is input stream completed. 
     * @return 
     */ 
     public boolean isCompleted() { 
      return completed; 
     } 

    } 

} 

다음은 .vbs 스크립트에서 출력을 읽는 예제이지만 Linux sh 스크립트에서도 비슷하게 작동합니다.

Process p = Runtime.getRuntime().exec("script"); 
p.waitFor(); 
String output = IOUtils.toString(p.getInputStream()); 
String errorOutput = IOUtils.toString(p.getErrorStream()); 

경고 : : 많은 출력을 생성하지 않는 프로세스에 대한

ShellExec exec = new ShellExec(true, false); 
    exec.execute("cscript.exe", null, true, 
     "//Nologo", 
     "//B",   // batch mode, no prompts 
     "//T:320",  // timeout seconds 
     "c:/my/script/test1.vbs", // unix path delim works for script.exe 
     "script arg 1", 
     "script arg 2", 
    ); 
    System.out.println(exec.getOutput()); 
4

, 나는 Apache IOUtils를 사용이 간단한 해결책은 충분하다 생각하지만, 프로세스 출력을 많이 생성하는 경우,이 방법 Process class JavaDoc에서 언급 한 것처럼 문제가 발생할 수 있습니다.

생성 된 하위 프로세스에는 자체 터미널이나 콘솔이 없습니다. 모든 표준 io (즉 stdin, stdout, stderr) 연산은 세 개의 스트림 (getOutputStream(), getInputStream(), getErrorStream())을 통해 상위 프로세스로 리디렉션됩니다. 부모 프로세스는 이러한 스트림을 사용하여 하위 프로세스에 입력을 제공하고 하위 프로세스에서 출력을 가져옵니다. 일부 기본 플랫폼은 표준 입력 및 출력 스트림에 대해 제한된 버퍼 크기만을 제공하므로 입력 스트림을 즉시 작성하지 않거나 하위 프로세스의 출력 스트림을 읽지 못하면 하위 프로세스가 블록화되고 심지어 교착 상태가 발생할 수 있습니다.

관련 문제