process.waitFor () कभी नहीं लौटता


95
Process process = Runtime.getRuntime().exec("tasklist");
BufferedReader reader = 
    new BufferedReader(new InputStreamReader(process.getInputStream()));
process.waitFor();

कृपया ध्यान दें कि जेएवीए 8 पर एक वेटफोर अधिभार है जो आपको निर्दिष्ट करता है कि एक टाइमआउट निर्दिष्ट करें। यह एक ऐसे मामले से बचने के लिए बेहतर विकल्प हो सकता है, जहां वेटफ़ोर कभी नहीं लौटता।
इकासो

जवाबों:


145

कई कारण हैं जो waitFor()वापस नहीं आते हैं।

लेकिन यह आमतौर पर इस तथ्य को उबालता है कि निष्पादित कमांड नहीं छोड़ता है।

यह, फिर से, कई कारण हो सकते हैं।

एक सामान्य कारण यह है कि प्रक्रिया कुछ आउटपुट का उत्पादन करती है और आप उचित धाराओं से नहीं पढ़ते हैं। इसका मतलब यह है कि बफर के पूर्ण होते ही प्रक्रिया अवरुद्ध हो जाती है और आपकी प्रक्रिया को पढ़ने के लिए जारी रखने का इंतजार करता है। बदले में आपकी प्रक्रिया दूसरी प्रक्रिया समाप्त होने का इंतजार करती है (जो कि ऐसा नहीं होगा क्योंकि यह आपकी प्रक्रिया का इंतजार करती है, ...)। यह एक शास्त्रीय गतिरोध की स्थिति है।

आपको प्रक्रियाओं को इनपुट स्ट्रीम से लगातार पढ़ने की ज़रूरत है ताकि यह सुनिश्चित हो सके कि यह ब्लॉक नहीं होता है।

वहाँ एक अच्छा लेख है जो सभी प्रकार के नुकसानों के बारे में बताता है Runtime.exec()और उनके आस-पास के तरीके दिखाता है, जिन्हें "जब Runtime.exec () नहीं होगा" (हाँ, लेख 2000 से है, लेकिन सामग्री अभी भी लागू होती है!)


7
यह उत्तर सही है लेकिन यह समस्या का निवारण करने के लिए एक कोड नमूने को याद करता है। उपयोगी कोड के लिए पीटर लॉरी के जवाब पर एक नज़र डालें, यह पता लगाने के लिए कि क्यों waitFor()नहीं लौटता है।
फॉरग्रे आर

83

ऐसा प्रतीत होता है कि आप इसके खत्म होने का इंतजार करने से पहले आउटपुट नहीं पढ़ रहे हैं। यह तभी ठीक होता है जब आउटपुट बफर को नहीं भरता है। यदि ऐसा होता है, तो यह तब तक इंतजार करेगा जब तक आप आउटपुट, कैच -22 को नहीं पढ़ लेते।

शायद आपकी कुछ त्रुटियां हैं जो आप नहीं पढ़ रहे हैं। यह अनुप्रयोग को रोकने और हमेशा के लिए प्रतीक्षा करने के लिए प्रतीक्षा करेगा। इसके आस-पास एक सरल तरीका यह है कि नियमित आउटपुट में त्रुटियों को फिर से निर्देशित किया जाए।

ProcessBuilder pb = new ProcessBuilder("tasklist");
pb.redirectErrorStream(true);
Process process = pb.start();
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while ((line = reader.readLine()) != null)
    System.out.println("tasklist: " + line);
process.waitFor();

4
जानकारी के लिए: ProcessBuilder एक वास्तविक बिल्डर होने के नाते, आप सीधे ProcessBuilder pb = new ProcessBuilder ("कार्यसूची") लिख सकते हैं। redirectErrorStream (true);
जीन-फ्रांस्वा सवार्ड

3
मैं बल्कि इस्तेमाल करता हूँpb.redirectError(new File("/dev/null"));
Toochka

@Toochka सिर्फ जानकारी के लिए, redirectErrorजावा 1.7 के बाद से ही उपलब्ध है
ZhekaKozlov

3
मेरे द्वारा स्वीकृत उत्तर होना चाहिए, मैंने अपने कोड को इसके साथ बदल दिया और यह तुरंत काम कर गया।
गेर्बन रामपार्ट

43

जावा डॉक्टर से भी:

java.lang

कक्षा प्रक्रिया

क्योंकि कुछ देशी प्लेटफ़ॉर्म केवल मानक इनपुट और आउटपुट स्ट्रीम के लिए सीमित बफर आकार प्रदान करते हैं, इनपुट स्ट्रीम को तुरंत लिखने में विफलता या उपप्रकार के आउटपुट स्ट्रीम को पढ़ने के कारण उपप्रकार ब्लॉक हो सकता है, और यहां तक ​​कि गतिरोध भी हो सकता है।

प्रक्रिया से इनपुट स्ट्रीम (जो सबप्रोसेस के आउटपुट स्ट्रीम में पाइप) को बफ़र करने में विफल हो सकता है, जिससे सबप्रोसेस ब्लॉक हो सकता है।

इसे इस्तेमाल करे:

Process process = Runtime.getRuntime().exec("tasklist");
BufferedReader reader =
new BufferedReader(new InputStreamReader(process.getInputStream()));
while ((reader.readLine()) != null) {}
process.waitFor();

17
दो कैविएट: (1) प्रोसैबबर्ल + पुनर्निर्देशन का उपयोग करें (सही), फिर आप सुरक्षित हैं। Else, (2) आपको Process.getInputStream () से पढ़ने के लिए एक थ्रेड की जरूरत है और दूसरे को Process.getErrorStream () से पढ़ने के लिए। बस इसे (!), उर्फ, "द हार्ड वे" का पता लगाने में लगभग चार घंटे लगे।
केविनरपे

1
आप Apache Commons Exec फंक्शनलिटी का उपयोग एक साथ stdout और stderr स्ट्रीम का उपभोग करने के लिए कर सकते हैं: DefaultExecutor executor = new DefaultExecutor(); PumpStreamHandler pumpStreamHandler = new PumpStreamHandler(stdoutOS, stderrOS); executor.setStreamHandler(pumpStreamHandler); executor.execute(cmdLine);जहाँ stoutOS और stderrOS BufferedOutputStreams हैं, मैंने उचित फ़ाइलों को लिखने के लिए बनाया है।
मैथ्यू वाइज

मेरे मामले में मैं स्प्रिंग से एक बैच फ़ाइल बुला रहा था जो आंतरिक रूप से एक संपादक को खोलता है। कोड लागू करने के बाद भी मेरा कोड लटका रहा process.close()। लेकिन जब मैं ऊपर के रूप में सुझाए गए इनपुट स्ट्रीम को खोलता हूं और तुरंत बंद कर देता हूं - समस्या दूर हो जाती है। तो मेरे मामले में स्प्रिंग स्ट्रीम क्लोज सिग्नल का इंतजार कर रहा था। हालांकि मैं जावा 8 ऑटो बंद करने योग्य उपयोग कर रहा हूं।
6

10

मैं पिछले उत्तरों में कुछ जोड़ना चाहूंगा लेकिन चूंकि मेरे पास टिप्पणी करने के लिए रिपीट नहीं है, मैं सिर्फ एक उत्तर जोड़ूंगा। यह उन Android उपयोगकर्ताओं की ओर निर्देशित है जो जावा में प्रोग्रामिंग कर रहे हैं।

रोलिंगबॉय के पोस्ट के अनुसार, इस कोड ने लगभग मेरे लिए काम किया:

Process process = Runtime.getRuntime().exec("tasklist");
BufferedReader reader =
new BufferedReader(new InputStreamReader(process.getInputStream()));
while ((reader.readLine()) != null) {}
process.waitFor();

मेरे मामले में, वेटफ़ोर () रिलीज़ नहीं हो रहा था क्योंकि मैं बिना रिटर्न वाले बयान को निष्पादित कर रहा था ("आईपी एडड्र फ्लश एथ0")। इसे ठीक करने का एक आसान तरीका यह है कि आप यह सुनिश्चित करें कि आप हमेशा अपने बयान में कुछ वापस करें। मेरे लिए, इसका मतलब है कि निम्नलिखित को निष्पादित करना: "आईपी एडड्र फ्लश एथ0 और& इको किया गया"। आप पूरे दिन बफर पढ़ सकते हैं, लेकिन अगर कभी कुछ वापस नहीं किया जाता है, तो आपका धागा कभी भी अपना इंतजार नहीं करेगा।

आशा है कि किसी की मदद करता है!


2
जब आपके पास टिप्पणी करने के लिए रिपीट न हो, तो इसके आसपास काम न करें और वैसे भी टिप्पणी करें । इसे स्वयं ही उत्तर दें और इससे दोहराएं!
निधि मोनिका का मुकदमा

मुझे नहीं लगता कि यह है process.waitFor()कि यह लटका है reader.readLine()कि अगर आप कोई उत्पादन नहीं है तो यह लटका हुआ है। मैंने waitFor(long,TimeUnit)टाइमआउट का उपयोग करने की कोशिश की अगर कुछ गलत हो गया और पता चला कि यह रीड द हंग था। जो टाइमआउट किए गए संस्करण को रीडिंग करने के लिए एक और थ्रेड की आवश्यकता होती है ...
osundblad

5

कई संभावनाएं हैं:

  1. आपने प्रक्रिया के सभी आउटपुट का उपभोग नहीं किया है stdout
  2. आपने प्रक्रिया के सभी आउटपुट का उपभोग नहीं किया है stderr
  3. प्रक्रिया आपसे इनपुट की प्रतीक्षा कर रही है और आपने इसे प्रदान नहीं किया है, या आपने प्रक्रिया को बंद नहीं किया है stdin
  4. प्रक्रिया एक कठिन लूप में घूम रही है।

5

जैसा कि दूसरों ने उल्लेख किया है कि आपको स्टेडर और स्टडआउट का सेवन करना होगा ।

अन्य उत्तरों की तुलना में, जावा 1.7 के बाद से यह और भी आसान है। आपको स्ट्रेडर और स्टडआउट को पढ़ने के लिए खुद को थ्रेड्स बनाने की आवश्यकता नहीं है ।

बस का उपयोग करें ProcessBuilderऔर या redirectOutputतो के साथ संयोजन में विधियों का उपयोग करें ।redirectErrorredirectErrorStream

String directory = "/working/dir";
File out = new File(...); // File to write stdout to
File err = new File(...); // File to write stderr to
ProcessBuilder builder = new ProcessBuilder();
builder.directory(new File(directory));
builder.command(command);
builder.redirectOutput(out); // Redirect stdout to file
if(out == err) { 
  builder.redirectErrorStream(true); // Combine stderr into stdout
} else { 
  builder.redirectError(err); // Redirect stderr to file
}
Process process = builder.start();

2

उसी कारण से आप inheritIO()बाहरी ऐप कंसोल जैसे जावा कंसोल मैप करने के लिए भी उपयोग कर सकते हैं जैसे:

ProcessBuilder pb = new ProcessBuilder(appPath, arguments);

pb.directory(new File(appFile.getParent()));
pb.inheritIO();

Process process = pb.start();
int success = process.waitFor();

2

आपको उसी समय में आउटपुट और त्रुटि का उपभोग करने की कोशिश करनी चाहिए

    private void runCMD(String CMD) throws IOException, InterruptedException {
    System.out.println("Standard output: " + CMD);
    Process process = Runtime.getRuntime().exec(CMD);

    // Get input streams
    BufferedReader stdInput = new BufferedReader(new InputStreamReader(process.getInputStream()));
    BufferedReader stdError = new BufferedReader(new InputStreamReader(process.getErrorStream()));
    String line = "";
    String newLineCharacter = System.getProperty("line.separator");

    boolean isOutReady = false;
    boolean isErrorReady = false;
    boolean isProcessAlive = false;

    boolean isErrorOut = true;
    boolean isErrorError = true;


    System.out.println("Read command ");
    while (process.isAlive()) {
        //Read the stdOut

        do {
            isOutReady = stdInput.ready();
            //System.out.println("OUT READY " + isOutReady);
            isErrorOut = true;
            isErrorError = true;

            if (isOutReady) {
                line = stdInput.readLine();
                isErrorOut = false;
                System.out.println("=====================================================================================" + line + newLineCharacter);
            }
            isErrorReady = stdError.ready();
            //System.out.println("ERROR READY " + isErrorReady);
            if (isErrorReady) {
                line = stdError.readLine();
                isErrorError = false;
                System.out.println("ERROR::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::" + line + newLineCharacter);

            }
            isProcessAlive = process.isAlive();
            //System.out.println("Process Alive " + isProcessAlive);
            if (!isProcessAlive) {
                System.out.println(":::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: Process DIE " + line + newLineCharacter);
                line = null;
                isErrorError = false;
                process.waitFor(1000, TimeUnit.MILLISECONDS);
            }

        } while (line != null);

        //Nothing else to read, lets pause for a bit before trying again
        System.out.println("PROCESS WAIT FOR");
        process.waitFor(100, TimeUnit.MILLISECONDS);
    }
    System.out.println("Command finished");
}

1

मुझे लगता है कि मैंने एक समान समस्या देखी: कुछ प्रक्रियाएं शुरू हुईं, लगता है कि वे सफलतापूर्वक चलेंगी लेकिन कभी पूरी नहीं हुईं। फ़ंक्शन प्रतीक्षाकर्ता () हमेशा इंतजार कर रहा था सिवाय अगर मैंने टास्क मैनेजर में प्रक्रिया को मार दिया।
हालाँकि, कमांड लाइन की लंबाई 127 वर्ण या उससे कम होने की स्थिति में सब कुछ ठीक रहा। यदि लंबी फ़ाइल नाम अपरिहार्य हैं, तो आप पर्यावरण चर का उपयोग करना चाह सकते हैं, जो आपको कमांड लाइन स्ट्रिंग को छोटा रखने की अनुमति दे सकता है। आप एक बैच फ़ाइल (FileWriter का उपयोग करके) उत्पन्न कर सकते हैं जिसमें आप अपने पर्यावरण चर को उस प्रोग्राम को कॉल करने से पहले सेट करते हैं जिसे आप वास्तव में चलाना चाहते हैं। इस तरह के एक बैच की सामग्री की तरह लग सकता है:

    set INPUTFILE="C:\Directory 0\Subdirectory 1\AnyFileName"
    set OUTPUTFILE="C:\Directory 2\Subdirectory 3\AnotherFileName"
    set MYPROG="C:\Directory 4\Subdirectory 5\ExecutableFileName.exe"
    %MYPROG% %INPUTFILE% %OUTPUTFILE%

अंतिम चरण रनटाइम का उपयोग करके इस बैच फ़ाइल को चला रहा है।


1

यहाँ एक विधि है जो मेरे लिए काम करती है। नोट: इस पद्धति के भीतर कुछ कोड है जो आपके लिए लागू नहीं हो सकता है, इसलिए इसे आज़माएं और अनदेखा करें। उदाहरण के लिए "logStandardOut (...), git-bash, आदि"।

private String exeShellCommand(String doCommand, String inDir, boolean ignoreErrors) {
logStandardOut("> %s", doCommand);

ProcessBuilder builder = new ProcessBuilder();
StringBuilder stdOut = new StringBuilder();
StringBuilder stdErr = new StringBuilder();

boolean isWindows = System.getProperty("os.name").toLowerCase().startsWith("windows");
if (isWindows) {
  String gitBashPathForWindows = "C:\\Program Files\\Git\\bin\\bash";
  builder.command(gitBashPathForWindows, "-c", doCommand);
} else {
  builder.command("bash", "-c", doCommand);
}

//Do we need to change dirs?
if (inDir != null) {
  builder.directory(new File(inDir));
}

//Execute it
Process process = null;
BufferedReader brStdOut;
BufferedReader brStdErr;
try {
  //Start the command line process
  process = builder.start();

  //This hangs on a large file
  // /programming/5483830/process-waitfor-never-returns
  //exitCode = process.waitFor();

  //This will have both StdIn and StdErr
  brStdOut = new BufferedReader(new InputStreamReader(process.getInputStream()));
  brStdErr = new BufferedReader(new InputStreamReader(process.getErrorStream()));

  //Get the process output
  String line = null;
  String newLineCharacter = System.getProperty("line.separator");

  while (process.isAlive()) {
    //Read the stdOut
    while ((line = brStdOut.readLine()) != null) {
      stdOut.append(line + newLineCharacter);
    }

    //Read the stdErr
    while ((line = brStdErr.readLine()) != null) {
      stdErr.append(line + newLineCharacter);
    }

    //Nothing else to read, lets pause for a bit before trying again
    process.waitFor(100, TimeUnit.MILLISECONDS);
  }

  //Read anything left, after the process exited
  while ((line = brStdOut.readLine()) != null) {
    stdOut.append(line + newLineCharacter);
  }

  //Read anything left, after the process exited
  while ((line = brStdErr.readLine()) != null) {
    stdErr.append(line + newLineCharacter);
  }

  //cleanup
  if (brStdOut != null) {
    brStdOut.close();
  }

  if (brStdErr != null) {
    brStdOut.close();
  }

  //Log non-zero exit values
  if (!ignoreErrors && process.exitValue() != 0) {
    String exMsg = String.format("%s%nprocess.exitValue=%s", stdErr, process.exitValue());
    throw new ExecuteCommandException(exMsg);
  }

} catch (ExecuteCommandException e) {
  throw e;
} catch (Exception e) {
  throw new ExecuteCommandException(stdErr.toString(), e);
} finally {
  //Log the results
  logStandardOut(stdOut.toString());
  logStandardError(stdErr.toString());
}

return stdOut.toString();

}


0

समय के साथ प्रतीक्षा से बचने के साथ संयुक्त धारा के अतुल्यकालिक पढ़ने से समस्या हल हो जाएगी।

आप इसे यहाँ समझाने वाला एक पृष्ठ पा सकते हैं http://simplebasics.net/.net/process-waitforexit-with-a-timeout-will-not-be-able-to-collect-the-output-message/

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.