इनपुट / आउटपुट स्ट्रीम के साथ जावा प्रक्रिया


90

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

ऐसा क्यों है या मैं कुछ गलत कर रहा हूं? मेरा अंतिम लक्ष्य एक थ्रेडेड शेड्यूल किए गए कार्य को बनाना है जो समय-समय पर कमांड को निष्पादित / बैश करता है OutputStreamऔर InputStreamउसे मिलकर काम करना होगा और काम करना बंद नहीं करना चाहिए। मैं भी java.io.IOException: Broken pipeकिसी भी त्रुटि त्रुटि का सामना कर रहा हूँ ?

धन्यवाद।

String line;
Scanner scan = new Scanner(System.in);

Process process = Runtime.getRuntime ().exec ("/bin/bash");
OutputStream stdin = process.getOutputStream ();
InputStream stderr = process.getErrorStream ();
InputStream stdout = process.getInputStream ();

BufferedReader reader = new BufferedReader (new InputStreamReader(stdout));
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(stdin));

String input = scan.nextLine();
input += "\n";
writer.write(input);
writer.flush();

input = scan.nextLine();
input += "\n";
writer.write(input);
writer.flush();

while ((line = reader.readLine ()) != null) {
System.out.println ("Stdout: " + line);
}

input = scan.nextLine();
input += "\n";
writer.write(input);
writer.close();

while ((line = reader.readLine ()) != null) {
System.out.println ("Stdout: " + line);
}

"टूटी हुई पाइप" का मतलब शायद बच्चे की प्रक्रिया से बाहर निकल गया है। अन्य कोड क्या हैं, यह देखने के लिए अपने बाकी कोड पर पूरी तरह से ध्यान नहीं दिया।
वंजा

1
अलग थ्रेड्स का उपयोग करें, यह ठीक काम करेगा
Johnydep

जवाबों:


139

सबसे पहले, मैं लाइन की जगह लेने की सलाह दूंगा

Process process = Runtime.getRuntime ().exec ("/bin/bash");

लाइनों के साथ

ProcessBuilder builder = new ProcessBuilder("/bin/bash");
builder.redirectErrorStream(true);
Process process = builder.start();

ProcessBuilder जावा 5 में नया है और बाहरी प्रक्रियाओं को चलाना आसान बनाता है। मेरी राय में, इसका सबसे महत्वपूर्ण सुधार Runtime.getRuntime().exec()यह है कि यह आपको अपने मानक आउटपुट में बच्चे की प्रक्रिया की मानक त्रुटि को पुनर्निर्देशित करने की अनुमति देता है। इसका मतलब है कि आपके पास InputStreamपढ़ने के लिए केवल एक ही है । इससे पहले, आपको मानक त्रुटि बफ़र भरने से बचने के लिए दो अलग-अलग थ्रेड्स, एक रीडिंग stdoutऔर एक रीडिंग की आवश्यकता थी stderr, जबकि मानक आउटपुट बफर खाली था (जिससे बच्चे की प्रक्रिया को लटका दिया गया था), या इसके विपरीत।

अगला, छोरों (जिनमें से आपके पास दो हैं)

while ((line = reader.readLine ()) != null) {
    System.out.println ("Stdout: " + line);
}

केवल तभी बाहर निकलें reader, जो प्रक्रिया के मानक आउटपुट से पढ़ता है, एंड-ऑफ-फाइल लौटाता है। यह केवल तब होता है जब bashप्रक्रिया से बाहर निकलता है। यदि प्रक्रिया से अधिक उत्पादन नहीं होता है, तो यह एंड-ऑफ-फाइल वापस नहीं आएगा। इसके बजाय, यह प्रक्रिया से आउटपुट की अगली पंक्ति की प्रतीक्षा करेगा और तब तक वापस नहीं आएगा जब तक कि यह अगली पंक्ति न हो।

चूंकि आप इस लूप तक पहुंचने से पहले प्रक्रिया को इनपुट की दो लाइनें भेज रहे हैं, इसलिए इन दो लूपों में से पहला लटक जाएगा यदि इनपुट के इन दो लाइनों के बाद प्रक्रिया बाहर नहीं निकली है। यह वहां दूसरी पंक्ति के पढ़ने की प्रतीक्षा में बैठेगा, लेकिन इसे पढ़ने के लिए दूसरी पंक्ति कभी नहीं होगी।

मैं अपने स्रोत कोड (मैं, इस समय विंडोज पर हूँ तो मैं प्रतिस्थापित संकलित /bin/bashसाथ cmd.exeहै, लेकिन सिद्धांतों ही होना चाहिए), और मुझे लगता है कि पाया:

  • दो लाइनों में टाइप करने के बाद, पहले दो कमांड से आउटपुट दिखाई देता है, लेकिन फिर प्रोग्राम हैंग हो जाता है,
  • यदि मैं टाइप करता हूं, echo testतो, और फिर exit, प्रोग्राम इसे पहले लूप से बाहर कर देता है क्योंकि cmd.exeप्रक्रिया से बाहर निकल गया है। फिर प्रोग्राम इनपुट की एक और लाइन (जिसे अनदेखा कर दिया जाता है) के लिए पूछता है, दूसरे लूप पर सीधे स्किप हो जाता है क्योंकि चाइल्ड प्रोसेस पहले ही निकल चुका होता है, और फिर खुद बाहर निकल जाता है।
  • यदि मैं टाइप करता हूं exitऔर फिर echo test, मुझे एक IOException मिलती है, जिसमें एक पाइप बंद होने की शिकायत होती है। यह अपेक्षित है - इनपुट की पहली पंक्ति के कारण प्रक्रिया से बाहर निकल गया, और दूसरी पंक्ति भेजने के लिए कहीं नहीं है।

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

मैंने आपका कोड लिया और मैंने उस पंक्ति के बाद सब कुछ बदल दिया writerजो निम्न लूप के साथ काम करता है :

while (scan.hasNext()) {
    String input = scan.nextLine();
    if (input.trim().equals("exit")) {
        // Putting 'exit' amongst the echo --EOF--s below doesn't work.
        writer.write("exit\n");
    } else {
        writer.write("((" + input + ") && echo --EOF--) || echo --EOF--\n");
    }
    writer.flush();

    line = reader.readLine();
    while (line != null && ! line.trim().equals("--EOF--")) {
        System.out.println ("Stdout: " + line);
        line = reader.readLine();
    }
    if (line == null) {
        break;
    }
}

ऐसा करने के बाद, मैं मज़बूती से कुछ कमांड चला सकता था और प्रत्येक से आउटपुट व्यक्तिगत रूप से मेरे पास वापस आ सकता था।

echo --EOF--शेल को भेजी गई पंक्ति में दो कमांड यह सुनिश्चित करने के लिए हैं कि कमांड से आउटपुट कमांड --EOF--से एक त्रुटि के परिणामस्वरूप भी समाप्त हो गया है।

बेशक, इस दृष्टिकोण की अपनी सीमाएं हैं। इन सीमाओं में शामिल हैं:

  • यदि मैं एक कमांड दर्ज करता हूं जो उपयोगकर्ता इनपुट की प्रतीक्षा करता है (उदाहरण के लिए एक और शेल), तो प्रोग्राम लटका हुआ प्रतीत होता है,
  • यह मानता है कि शेल द्वारा संचालित प्रत्येक प्रक्रिया एक नई रेखा के साथ अपने उत्पादन को समाप्त करती है,
  • यह थोड़ा गड़बड़ हो जाता है अगर शेल द्वारा चलाई जा रही कमांड लाइन लिखने के लिए होती है --EOF--
  • bashएक वाक्यविन्यास त्रुटि की रिपोर्ट करता है और यदि आप किसी बेजोड़ के साथ कुछ पाठ दर्ज करते हैं तो बाहर निकलता है )

ये बिंदु आपके लिए कोई मायने नहीं रखते अगर आप जो कुछ भी सोच रहे हैं उसे एक निर्धारित कार्य के रूप में चलाने की आज्ञा या एक छोटे से आदेश तक ही सीमित रहने जा रहे हैं जो कभी भी इस तरह के रोग संबंधी तरीकों से व्यवहार नहीं करेगा।

EDIT : लिनक्स पर इसे चलाने के बाद निकास से निपटने और अन्य छोटे बदलावों में सुधार करें


व्यापक उत्तर के लिए धन्यवाद, हालांकि, मुझे लगता है कि मैंने अपनी समस्याओं के लिए सच्चे मामले की पहचान की है। अपनी पोस्ट में मेरे ध्यान में लाया। stackoverflow.com/questions/3645889/… । धन्यवाद।
जेम्स ने

1
cmd के साथ / बिन / बैश की जगह वास्तव में एक ही व्यवहार नहीं किया, जैसा कि मैंने प्रोग्राम बनाया जो कि वही था लेकिन बैश के साथ समस्या थी। मायकेस में, प्रत्येक इनपुट / आउटपुट के लिए तीन अलग थ्रेड खोलना / लंबे समय तक इंटरैक्टिव कमांड के लिए किसी भी समस्या के बिना सबसे अच्छा काम करता है।
जॉनयडेप


@AlexMills: क्या आपकी दूसरी टिप्पणी का मतलब है कि आपने अपनी समस्या हल कर ली है? आपकी पहली टिप्पणी में पर्याप्त विवरण नहीं है कि समस्या क्या है, और न ही आप 'अचानक' अपवाद क्यों पा रहे हैं।
ल्यूक वुडवर्ड

@ ल्यूक, आपकी टिप्पणी के ठीक ऊपर प्रदान किया गया लिंक मेरी समस्या का समाधान करता है
अलेक्जेंडर मिल्स

4

मुझे लगता है कि आप अपने इनपुट को पढ़ने के लिए दानव-धागे की तरह धागे का उपयोग कर सकते हैं और आपका आउटपुट रीडर पहले से ही मुख्य धागे में लूप में होगा ताकि आप एक ही समय में पढ़ सकें और लिख सकें। आप इस तरह से अपने कार्यक्रम को संशोधित कर सकते हैं:

Thread T=new Thread(new Runnable() {

    @Override
    public void run() {
        while(true)
        {
            String input = scan.nextLine();
            input += "\n";
            try {
                writer.write(input);
                writer.flush();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

        }

    }
} );
T.start();

और आप पाठक ऊपर के रूप में ही हो जाएगा

while ((line = reader.readLine ()) != null) {
    System.out.println ("Stdout: " + line);
}

अपने लेखक को अंतिम रूप दें अन्यथा यह आंतरिक वर्ग द्वारा सुलभ होने में सक्षम नहीं होगा।


1

आपके पास writer.close();आपके कोड में है इसलिए बैश को ईओएफ मिलता है stdinऔर वह बाहर निकल जाता है। तब आप डिफंक्शन बैश Broken pipeसे पढ़ने की कोशिश करते हैं stdout

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