2009-05-19 8 views
50

बुला जब ग्राहक की मशीनों पर नेटवर्किंग समस्याओं का सामना कर, मैं खुद के लिए कुछ कमांड लाइन उनमें से परिणाम चलाने के लिए और ईमेल करने के लिए सक्षम होने के लिए करना चाहते हैं।कैप्चरिंग stdout जब Runtime.exec

मैंने पाया Runtime.exec मुझे मनमाना आदेश पर अमल करने की अनुमति देगा, लेकिन एक स्ट्रिंग में परिणाम एकत्रित किए और अधिक रोचक है।

मुझे पता है मैं एक फाइल करने के लिए उत्पादन अनुप्रेषित सकता है, और फिर फ़ाइल से पढ़ने, लेकिन मेरे spidey भावना वहाँ यह करने का एक और अधिक सुरुचिपूर्ण तरीका है मुझे बता रहा है।

सुझाव?

+0

इस [लेख] (http://www.javaworld.com/javaworld/jw-12-2000/jw-1229-traps.html) पर एक नज़र डालें। – kgiannakakis

उत्तर

46

आप इस प्रक्रिया में दोनों एसटीडी बाहर और एसटीडी अं पर कब्जा करने की जरूरत है। फिर आप एक फ़ाइल/मेल या इसी तरह के std बाहर लिख सकते हैं।

अधिक जानकारी के लिए this article देखें, और विशेष रूप नोट में StreamGobbler तंत्र कि कब्जा अलग धागे में stdout/अं। अवरोध को रोकने के लिए यह आवश्यक है और यदि आप इसे सही तरीके से नहीं करते हैं तो कई त्रुटियों का स्रोत है!

+0

मैंने देखा कि यदि आदेश बहुत अधिक आउटपुट देता है तो गोबब्लर्स आउटपुट करने से पहले कोड प्रवाह जारी रहेगा। लौटने से पहले उन पर jjoin() को कॉल करने की आवश्यकता है। – Zitrax

+0

_Extremely_ प्रभावी और सरल विधि। हालांकि ध्यान देने योग्य बात यह है कि मुख्य विधि में 'cmd' सरणी प्रारंभिकरण विंडोज 7 के लिए थोड़ा दिनांकित प्रतीत होता है। अंतिम "अन्य" खंड जोड़ें जो एनटी शैली में cmd ​​सरणी को प्रारंभ करता है, भले ही अन्य ' अन्यथा अगर (osName.equals ("विंडोज एनटी") वापस झूठ बोलता है। – depthfirstdesigner

+0

क्या स्टीमगोब्बलर के साथ समाधान केवल विंडोज सर्वर के लिए है? अगर मैं यूनिक्स का उपयोग कर रहा हूं- ऐसा नहीं होगा? – Dejell

13

ProcessBuilder का उपयोग करें। कॉल करने के बाद() आपको Process ऑब्जेक्ट मिलेगा जिससे आप stderr और stdout स्ट्रीम प्राप्त कर सकते हैं।

अद्यतन: प्रक्रियाबिल्डर आपको अधिक नियंत्रण देता है; आपको इसका उपयोग करने की ज़रूरत नहीं है लेकिन मुझे लंबे समय तक यह आसान लगता है। विशेष रूप से stderr को stdout पर रीडायरेक्ट करने की क्षमता जिसका अर्थ है कि आपको केवल एक स्ट्रीम को चूसना है।

+3

और मैं अपने आउटपुट को 'आउटपुटस्ट्रीम' से कैसे प्राप्त कर सकता हूं? – pihentagy

2

Runtime.exec() एक प्रक्रिया वस्तु, जिसमें से आप उत्पादन जो कुछ भी की कमान आप भाग गया निकाल सकते हैं देता है।

1

Runtime.exec का उपयोग करके आपको एक प्रक्रिया मिलती है। आप इस प्रक्रिया के stdout प्राप्त करने के लिए getInputStream का उपयोग कर सकते हैं, और उदाहरण के लिए एक स्ट्रिंगबफर के माध्यम से, इस इनपुट स्ट्रीम को स्ट्रिंग में डाल दें।

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. यदि आप मतदान करते हैं, तो आपको आउटपुट पढ़ने के लिए प्रतीक्षा करते समय इसे संभालने के लिए कोड जोड़ना होगा। प्लेक्सस यूटिलस जैसे पुस्तकालयों का उद्देश्य - 246k- आपको फिर से पहिया को फिर से शुरू करने में मदद करने के लिए है :) –

+0

चींटी वही काम करती है, यदि आप चाहें तो इसका उपयोग कर सकते हैं, एक मुख्य कार्य है जिसे कहा जा सकता है (उचित प्रारंभिक चींटी संदर्भ के साथ) इस कार्य को करने के लिए, लेकिन मैं छोटे से प्लेक्सस उपयोग पसंद करता हूं (आप क्ली पैकेज को छोड़कर सबकुछ भी निकाल सकते हैं, जिसका अर्थ है कि आपके पास 50k से कम होगा), समर्पित और प्रमाण स्थिर होना चाहिए (चूंकि मेवेन 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 स्क्रिप्ट लेकिन लिनक्स श स्क्रिप्ट के लिए इसी तरह के कार्यों से उत्पादन पढ़ने है।

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 p = Runtime.getRuntime().exec("script"); 
p.waitFor(); 
String output = IOUtils.toString(p.getInputStream()); 
String errorOutput = IOUtils.toString(p.getErrorStream()); 

चेतावनी: हालांकि, अगर आपके प्रक्रिया उत्पादन का एक बहुत उत्पन्न करता है, इस दृष्टिकोण Process class JavaDoc में उल्लिखित समस्याओं का कारण हो सकता है:

निर्मित उपप्रोसेसर का अपना टर्मिनल या कंसोल नहीं है। इसके सभी मानक io (यानी stdin, stdout, stderr) संचालन को तीन धाराओं (getOutputStream(), getInputStream(), getErrorStream()) के माध्यम से मूल प्रक्रिया में रीडायरेक्ट किया जाएगा। अभिभावक प्रक्रिया इनपुट को फ़ीड करने और उपप्रोसेसर से आउटपुट प्राप्त करने के लिए इन धाराओं का उपयोग करती है। चूंकि कुछ देशी प्लेटफ़ॉर्म केवल मानक इनपुट और आउटपुट स्ट्रीम के लिए सीमित बफर आकार प्रदान करते हैं, इनपुट स्ट्रीम को तत्काल लिखने में विफलता या उपप्रोसेस की आउटपुट स्ट्रीम को पढ़ने से उपप्रोसेस अवरुद्ध हो सकता है, और यहां तक ​​कि डेडलॉक भी हो सकता है।